Is it a good practice to work with Tags to access instances from different scripts or is it necessary to make an instance manager script?

I’m a beginner and I have a Drag and Drop function in some of my object instances but my main controller script can’t interact with the instances since they are created in the drag and drop script, so im using tags to find them, and now there is a function to have proper tags for each instance according to their slot inside the drag and drop script which feels wrong because it should be in the controller (im making a multiplayer card game and the controller is managing the game board with cards being instantiated as minions when they are dragged from my UI and dropped to the board)
So as general advice should I continue using tags or will I run into problems with this architecture ?

@Captain_Pineapple thank you a lot with your fast answer, so here is the drag and drop script that i put in the cards elements this code is not so important here but i just put it in case it helps understanding the code or if someone wanna use it lol

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class DragDrop : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler {
    
	private RectTransform rectTransform;
	private CanvasGroup canvasGroup;

	private Vector3 startpoint;
	private Vector3 endpoint;
	private LineRenderer lr;
	private Camera cam;
	private Vector3 camOffset = new Vector3(0, 0, 10);
	
	private void Awake() {
		rectTransform = GetComponent<RectTransform>();
		canvasGroup = GetComponent<CanvasGroup>();
	}
	
	public void OnBeginDrag(PointerEventData eventData) {
		Debug.Log("Begin");
		canvasGroup.alpha = .6f;
		canvasGroup.blocksRaycasts = false;

		/*cam = Camera.main;
		if (lr == null)
        {
			Debug.Log("lul");
			lr.GameObject.AddComponent(typeof(LineRenderer)) ();
        }
		lr.positionCount = 2;
		startpoint = cam.ScreenToWorldPoint(Input.mousePosition)+camOffset;
		lr.SetPosition(0, startpoint);
		lr.useWorldSpace = true; */
		//startpoint.z = 15;
		Debug.Log(startpoint); 
	}
	
	public void OnDrag(PointerEventData eventData) {
		if (GetComponent<CardDisplay>().spell is SpellCardModel)
        {

        }
		else if (GetComponent<CardDisplay>().minion is CreatureCardModel)
        {
			rectTransform.anchoredPosition += eventData.delta;
		}
		
	}
	
	public void OnEndDrag(PointerEventData eventData) {
		Debug.Log("End");
		canvasGroup.alpha = 1f;
		canvasGroup.blocksRaycasts = true;
	}
	
	public void OnPointerDown(PointerEventData eventData) {
		Debug.Log("OnPointerDown");
		//throw new System.NotImplementedException();
	}
	
}

and this one is the script that i put in the board slots so when i drag something into them it enters a different function than if its a deck slot (according to the “puttodeck” variable value set in the 3D project) the instantiation happens in the “if puttodeck ==3” near the end

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ItemSlot : MonoBehaviour, IDropHandler {
	public int puttodeck;
	public Deck deckinsert = new Deck();
	public Text decklist;

	public GameObject playerPrefab;
	public GameObject enemyPrefab;
	public CardDisplay playerCard;
	CardDisplay enemyCard;

	public int slot;

	public void OnDrop(PointerEventData eventData) {
		Debug.Log("OnDrop");
		if (eventData.pointerDrag != null) {

			if (eventData.pointerDrag.GetComponent<CardDisplay>().spell == null)
			{
				eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = GetComponent<RectTransform>().anchoredPosition;
			}

			if (puttodeck == 1)
			{
				CardModel tmp = new CardModel(1, "hi", "hola", 2);
				if (eventData.pointerDrag.GetComponent<CardDisplay>().minion != null)
				{
					tmp = eventData.pointerDrag.GetComponent<CardDisplay>().minion;
					deckinsert.addcard(tmp);
					decklist.GetComponent<Text>().text += "

";
decklist.GetComponent().text += eventData.pointerDrag.GetComponent().minion.Name;
Destroy(eventData.pointerDrag);
}
else if (eventData.pointerDrag.GetComponent().spell != null)
{
tmp = eventData.pointerDrag.GetComponent().spell;
deckinsert.addcard(tmp);
decklist.GetComponent().text += "
";
decklist.GetComponent().text += eventData.pointerDrag.GetComponent().spell.Name;
Destroy(eventData.pointerDrag);
}

			}
			else if (puttodeck == 2)
			{
				int dmg = new int();
				int newhp = new int();
				if (eventData.pointerDrag.GetComponent<CardDisplay>().spell is SpellCardModel)
				{
					dmg = eventData.pointerDrag.GetComponent<CardDisplay>().spell.value;
					string oldhp = GetComponent<CardDisplay>().Healthtext.text;
					int.TryParse(oldhp, out newhp);
					newhp -= dmg;
					GetComponent<CardDisplay>().Healthtext.text = newhp.ToString();
					Destroy(eventData.pointerDrag.gameObject);
					if (newhp <= 0)
					{
						Destroy(gameObject);
					}
				}
				else if (eventData.pointerDrag.GetComponent<CardDisplay>().minion is CreatureCardModel)
				{
					dmg = eventData.pointerDrag.GetComponent<CardDisplay>().minion.Attack;
					string oldhp = GetComponent<CardDisplay>().Healthtext.text;
					int.TryParse(oldhp, out newhp);
					newhp -= dmg;
					GetComponent<CardDisplay>().Healthtext.text = newhp.ToString();
					Destroy(eventData.pointerDrag.gameObject);
					if (newhp <= 0)
					{
						Destroy(gameObject);
					}
				}
			}

			else if (puttodeck == 3 && eventData.pointerDrag.GetComponent<CardDisplay>().spell == null)
			{
				Transform tmp = GetComponent<RectTransform>();
				GameObject playerGO = Instantiate(playerPrefab, tmp);
				playerCard = playerGO.GetComponent<CardDisplay>();

				playerGO.GetComponent<CardDisplay>().minion = eventData.pointerDrag.GetComponent<CardDisplay>().minion as CreatureCardModel;
				playerGO.GetComponent<CardDisplay>().start();
				playerGO.gameObject.tag = "Ally"+slot;
				playerCard.start();

				Destroy(eventData.pointerDrag.gameObject);
			}
		}
	}
	
}

And here is the board controller, it might be messy but its on the button clicked function that it looks for the tags to put them into the ennemy’s turn’s actions, I dont have an advanced AI since its gonna be a multiplayer game so this is just to test the attack against instantiated cards, also, since attacking an ennemy by drag and dropping our minion on it doesnt require instantiation, the code doesnt use tags because it takes the object directly from the drag event, i dont know if its an acceptable practice

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public enum BattleState {START, PLAYERTURN, ENEMYTURN, WON, LOST}

public class Board : MonoBehaviour
{
    public Image Imageinputblock;
    public Deck pioche = new Deck();
    public Deck opponentDeck = new Deck();
    public Hand main = new Hand();
    public CardDisplay affichage = new CardDisplay();
    public CardDisplay myhero = new CardDisplay();
    public int HandSize = new int();

    public GameObject playerPrefab;
    public GameObject enemyPrefab;
    public Transform playerBattleStation;
    public Transform enemyBattleStation;

    CardDisplay playerCard;
    CardDisplay enemyCard;

    public Text GameState;

    public BattleState state;
    // ----------------------------TURN BASED SYSTEM--------------------------------000000000000

    void Start()
    {
        Imageinputblock.gameObject.SetActive(true);
        state = BattleState.START;
        StartCoroutine(SetupBattle());
    }

    IEnumerator SetupBattle()
    {
        GameState.text = "Opponent Thinking...";
        yield return new WaitForSeconds(4f);

        
        GameObject enemyGO = Instantiate(enemyPrefab, enemyBattleStation);
        enemyCard = enemyGO.GetComponent<CardDisplay>();

        
        enemyGO.GetComponent<CardDisplay>().minion = affichage.minion as CreatureCardModel;
        enemyGO.GetComponent<CardDisplay>().start();

        yield return new WaitForSeconds(1f);

        state = BattleState.PLAYERTURN;
        StartCoroutine(PlayerTurn());
    }

    IEnumerator WON()
    {
        GameState.text = "YOU WON !!";
        yield return new WaitForSeconds(1f); //there will be an animation

    }

    IEnumerator LOST()
    {
        GameState.text = "YOU LOST...";
        yield return new WaitForSeconds(1f); //there will be an animation

    }

    IEnumerator PlayerTurn()
    {
        //Board.IsInputEnabled = true; //enables all inputs
        Imageinputblock.enabled = false;
        if (GameObject.Find("CarteUNE").GetComponent<DragDrop>() != null)
        {
            GameObject.Find("CarteUNE").GetComponent<DragDrop>().enabled = true;
        }
        yield return new WaitForSeconds(1f); //there will be an animation
        GameState.text = "Your turn";


    }

    IEnumerator EnemyTurn()
    {
        Imageinputblock.enabled = true;
        GameState.text = "Opponent Thinking...";
        if (GameObject.Find("CarteUNE") != null)
        {
            GameObject.Find("CarteUNE").GetComponent<DragDrop>().enabled = false;
        }
        yield return new WaitForSeconds(2f);
        if (enemyCard != null)
        {
            if (playerCard != null)
            {
                int dmg = new int();
                int newhp = new int();
                dmg = enemyCard.minion.Attack;
                string oldhp = playerCard.Healthtext.text;
                int.TryParse(oldhp, out newhp);
                newhp -= dmg;
                playerCard.Healthtext.text = newhp.ToString();
                GameState.text = "Your " + playerCard.minion.name + " took damage";
                if (newhp <= 0)
                {
                    GameState.text = "Enemy " + enemyCard.minion.name + " killed your " + playerCard.minion.name;
                    Destroy(playerCard.gameObject);
                }
            }
            else
            {
                int dmg = new int();
                int newhp = new int();
                dmg = enemyCard.minion.Attack;
                string oldhp = myhero.Healthtext.text;
                int.TryParse(oldhp, out newhp);
                newhp -= dmg;
                myhero.Healthtext.text = newhp.ToString();
                GameState.text = "Your hero took damage";
                if (newhp <= 0)
                {
                    GameState.text = "Enemy " + enemyCard.minion.name + " killed your hero";
                    Destroy(myhero.gameObject);
                    yield return new WaitForSeconds(2f);
                    state = BattleState.LOST;
                    StartCoroutine(LOST());
                }
            }

            
        }
        else
        {
            int Decksize = new int();
            Decksize = opponentDeck.MyDeck.Count;
            CardModel carde = new CardModel(1, "test", "glitch", 9);


            carde = opponentDeck.getrandom(Decksize);
            GameObject enemyGO = Instantiate(enemyPrefab, enemyBattleStation);
            enemyGO.GetComponent<CardDisplay>().minion = carde as CreatureCardModel;
            enemyCard = enemyGO.GetComponent<CardDisplay>();
            enemyGO.GetComponent<CardDisplay>().start();
        }
        

        
        yield return new WaitForSeconds(4f);
        GameState.text = "Your turn";
        state = BattleState.PLAYERTURN;
        Imageinputblock.enabled = false;
        PlayerTurn();
    }


    // ----------------------------HAND AND DECK ACTIONS--------------------------------11111

    public void afficher()
    {
        //if (affichage.minion is CreatureCardModel)
        //affichage.minion = main.carte as CreatureCardModel;
        affichage.start();
    }

    IEnumerator piocher()
    {
        int Decksize = new int();
        Decksize = pioche.MyDeck.Count;
        CardModel carte = new CardModel(1, "test", "glitch", 9);
        HandSize = main.MyHand.Count;

        //carte = pioche.getcard();
        carte = pioche.getrandom(Decksize);
        main.addcard(carte);
        main.showhand();
        //main.addcard(pioche.MyDeck[HandSize]);

        //playerCard.minion = carte as CreatureCardModel;
        //playerCard.start();
        yield return new WaitForSeconds(2f);
    }

    // --------------------------------BUTTONS------------------------------------------010101010101001

    public void OnDrawButton()
    {
        if (state != BattleState.PLAYERTURN)
            return;
        StartCoroutine(piocher());
    }

    public void OnEndTurnButton()
    {
        if (state != BattleState.PLAYERTURN)
            return;
        
        if (GameObject.FindWithTag(tag: "Ally2") !=null) //here is the tags i use
        {
            playerCard = GameObject.FindWithTag(tag: "Ally2").GetComponent<CardDisplay>();
        }
        if (affichage == null)
        {
            state = BattleState.WON;
            StartCoroutine(WON());
        }
        else
        {
            state = BattleState.ENEMYTURN;
            StartCoroutine(EnemyTurn());
        }
        
    }

}

and this is just a picture of the board to get a general idea (sorry for the resolution the website wont let me have better quality, and also the art wont stay like that so no copyright problems i’ll change it hahah) thank you so much for your patience :smiley:
193778-275366421-940720166620301-5748551315540287401-n.png

From my past experience, avoiding Tags when possible is best.

Using tags means having to do a string comparison which is not performance-friendly and it’s prone to errors.

I honestly didn’t really understand what your current structure is, so my best bet is to probably have some Singleton responsible for creating the cards so that you can ask it to create a card and you can also use Events to notify others when a card is created.