Error in different buttons, with the same script

Good evening, everyone.

I’m stuck with a problem, let’s see if you can help me.

As you can see I’m making a game, in which you have to manage supplies and resources, advance in your planet and improve it, both in resources and creating defenses and attack. In it, I want to simplify the code as much as possible, making that from the same manager, I can control each element of it, obviously, differentiating each section of each planet, that is to say, if it belongs to resources (mines and warehouses. If it belongs to defences, to the defences, etc.). Honestly, I have never done something like this, I would like to know what you think about it, if you see it right or if it is better to do it in another way. My idea is that with this script, I can manage all the resources in this script, adding all the resources I want.

But I’m stuck and my problem is that I have upgrade buttons, in almost all the resources, stores, etc. My problem is that I want to recognise, which button is being pressed, if it belongs to a stone store, to a food mine, since you share script, that’s my main problem. I give you the code so you can have a look at it. I am Spanish, in case you find it hard to read the translated text. If you have any doubt, please let me know. Thanks in advance!

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


public class CaracteristicasMinas : MonoBehaviour
{
    public Mejora mejora;
    public GameObject panelSeguro;
    public TextMeshProUGUI panelMejora;
    public string strin;

  
    [System.Serializable]
    public class Minas
    {
        public Sprite image;
        public string nombre;
        public Button button;
        public float produccion, almacen, multi;
        public bool mejora;
        public int habitantes, mejorasuma, nMejora;
        public TextMeshProUGUI panelTexto;
        public InputField cntHabitantes;
        public List<Mejora> mejorarMina = new List<Mejora>();
        public GameObject panelInfo;

       
    }
 
    [System.Serializable]
    public class Mejora
    {
        public List<int> materiales;
       
    }
    public List<Minas> recursos = new List<Minas>();

   
   
    public  void FuncionamientoMinas()
    {
       
       
        for (var i = 0; i < recursos.Count; i++)
        {
           
            recursos[i].habitantes = int.Parse(recursos[i].cntHabitantes.text);
            recursos[i].produccion = recursos[i].produccion + (recursos[i].multi * recursos[i].habitantes);
            recursos[i].produccion = Mathf.Ceil(recursos[i].produccion);
            recursos[i].panelTexto.text = recursos[i].produccion.ToString();
            if(recursos[i].produccion > 10000 && recursos[i].produccion < 1000000)
            {
               
                recursos[i].panelTexto.text = Mathf.Round(recursos[i].produccion / 1000).ToString()+"K";

            }
            if (recursos[i].produccion > 1000000)
            {

                recursos[i].panelTexto.text = Mathf.Round(recursos[i].produccion / 1000000).ToString() + "M";

            }
            if (recursos[i].almacen < recursos[i].produccion)
            {
                recursos[i].produccion = recursos[i].almacen;
                recursos[i].panelTexto.color = Color.red;
                recursos[i].panelTexto.text = recursos[i].produccion.ToString();
               
            }
           
           
            /* if (recursos[i].produccion <= recursos[i].subMejoras[recursos[i].mejorasuma])
             {
                 recursos[i].botonMejora.interactable = false;
             }
             else
                 recursos[i].botonMejora.interactable = true;*/

            /* if (recursos[i].produccion >= recursos[i].subMejoras[recursos[i].mejorasuma] && recursos[i].mejora == true)
             {
                 recursos[i].produccion = recursos[i].produccion - recursos[i].subMejoras[recursos[i].mejorasuma];
                 recursos[i].mejorasuma = recursos[i].mejorasuma + 1;
                 recursos[i].mejora = false;
             }*/
        }
 
    }

     public void PanelMejorar()
     {
         panelSeguro.SetActive(true);
        //EventSystem.current.currentSelectedGameObject.gameObject.transform.parent.name;
        /* for (var i = 0; recursos[i].button.i; i++)
          {
             // if (recursos[i].button.GetComponent<Button>().onClick == true)

             //panelMejora.text = "¿Quieres mejorar por " + recursos[recursos[1].nMejora].mejorarMina[recursos[i].nMejora].materiales[recursos[i].nMejora].ToString() + "?";

         //PROBAR ESTO

         usando UnityEngine ;
usando System.Collections ;
utilizando UnityEngine.EventSystems ;

public class MyButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler {

public bool buttonPressed ;

public void OnPointerDown ( PointerEventData eventData ) {
      buttonPressed = verdadero ;
}

public void OnPointerUp ( PointerEventData eventData ) {
     buttonPressed = falso ;
}
}




          }*/


    }

    public void setupBtn()
    {
        for (var i = 0; i < recursos.Count; i++)
        {
            string param = "bar";
           // recursos[i].button.GetComponent<Button>().onClick.AddListener(delegate {btnClicked(param); });
        }
    }

    public void btnClicked()
    {
        Debug.Log(EventSystem.current.currentSelectedGameObject.name);
    }
    public void CancelarMejor()
    {
       
            panelSeguro.SetActive(false);

    }
    public void mejorarBoton()
    {
        for (var i = 0; i < recursos.Count; i++)
        {
            recursos[i].mejora = true;

        }

    }
    public void infoBoton()
    {
      
        for (var i = 0; i < recursos.Count; i++)
        {
            recursos[i].panelInfo.SetActive(true);

        }

    }

}

Hello, as you had set your buttons on an array (unless you had predefined beforehand that each button will always be in the same place, I mean the first button is for the store, second to defense and so on) you need a way to decide wich button is wich an enum into your “Minas” class will solve the problem

using System;
using UnityEngine;
using UnityEngine.UI;

public class Example : MonoBehaviour
{
    public enum KindOfMinas
    {
        Store,
        Resources,
        Etc
    }
   
    [Serializable]
    public class Minas
    {
        //.. your things

        public Button button;
        public KindOfMinas kindOfMinas;
    }

    // You will call this on your FuncionamentoMinas method
    public void AddBehaviourToButton (Minas minas)
    {
        var kind = minas.kindOfMinas;
        var button = minas.button;
       
        switch (kind)
        {
            case KindOfMinas.Store:
                button.onClick.AddListener(() =>
                {
                    // Do store thing here
                });
                break;

            case KindOfMinas.Resources:
                // You can also subdivide into methods
                button.onClick.AddListener(OnClickInResourcesButton);
                break;

            case KindOfMinas.Etc:
                // repeat
                break;

            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    private void OnClickInResourcesButton ()
    {
        // Do Resources things here
    }
}

This will solve your problem, but only as a hint [quote=“dama1994, post:1, topic: 857509, username:dama1994”]
In it, I want to simplify the code as much as possible, making that from the same manager
[/quote]
this isn’t true for most cases, this kind of approach will end giving to you some kind of god class that is hard to debug and couples all your code. Only as a suggestion, instead of making a class that control every button why not give each button the ability to control itself? If you create a simple class that adds behavior to a button and then inherits it with a class that actually implements the OnClick behavior, the thing will work smoothly and if some button gives you a problem, your class will have the size of the behavior only, this will make you spent less time debugging. But again, it’s only a hint, do it in that way that you think is better

Good afternoon.

Let’s see, I’m not an expert programmer, but I think it is well understood what I wanted to put, or maybe not, I’ll explain it a little better. It could also be that I didn’t understand it.

The game is an interface game, that is to say, there are only buttons, there is nothing graphic. My idea is to have a planet, in which you have a main menu, which is then divided into other submenus, which are; resources, defense, attack or fleet, (I may add more) etc… And in each submenu, there are other submenus that have their upgrade buttons. I was thinking, to make a script for the resources menu, because at the end, the mines and the warehouses are related for each section (resources, defences, fleets). In the screenshot I put in my post, it says “Food” which is food, but there would go, stone, iron, etc… And this I said before in a submenu of the resources and then another submenu of the mines, which at the same time, in the mines will have that menu that is the screenshot that I have passed in the first message. In warehouses you will also have a warehouse for each resource used.

When I read your message and processed it, I understood what you meant, but you said it to differentiate between, resource, defence or attack using an enum and in each enum put the characteristics you want each one to have. No? The truth is that I had not thought about it, it may be that everything goes to a script, but it may be that I get too tangled, I do not know.

My idea is to make a script for each submenu, that is, defence, attack, and resource menus, which are the most generic. In the end, if I improve a turret that is worth 30 iron, for example, it will have repercussions in the resource script, it makes it lose the amount of iron it has, for example, that’s why I want these scripts to be separated and go through a GameManager, where everything is controlled.

I hope I have expressed myself well and you have understood it, it is difficult for me to do it in a forum and in English.
Thank you very much for everything!

I didn’t understand exactly what is your doubt, I mean

This will work, as a personal preference, I would make scripts for buttons, not for submenus, because, if I understood right your buttons will have some kind of upgrades, and you probably will have customs upgrades at some point and if you let your submenu script take care about each upgrade, your scripts will end bigger than the necessary, but your approach will work too.

Only try to keep your data separate from your menus, I mean, HUD that shows your resources does not contains your resources it only shows it, sorry if this sounds basic but I got the impression in your text that your “resource menu” will contain your data.

If you think it’s necessary I can create a small demo project, only let me know

Ok, so you would do the button functions for each button in specialised button scripts.

Anyway, my problem is that I wanted to know if from a line of code, you can differentiate which button I’m using, with the same script for each resource I have, ie, each resource has the same script and the same assignment of your button that corresponds to improvement, but when you click, all are pressed and my question is if you can know when you click which button is.

If this is not possible, I have a script that is to move through the menu, and I will include there, the functions of the buttons, one by one.

If you don’t mind doing a little demo, go ahead. All the better to learn.

Thank you very much as always.

I think that you’re misunderstanding things here, you will not do some kind of check when each button is pressed to try to identify which button is being pressed, tbh this does not really make sense.

This also sounds strange to me, are you saying that you have multiple buttons and when you press one all of them are pressed? If this is the case something is really wrong with your setup

I’m don’t have a unity to run at moment, so I’ll try to sketch an example and if the doubt still on we can create and share some real examples on monday.

First, let me say that you can actually use the code that I’ve posted in the first post, in that case, we are using enums to change button behaviors, so for example, if this button will upgrade food, you will set the enum to food in the inspector, you’ll need to make some more work to differentiate between multiples buttons that make food upgrade, but that can totally work.

We can achieve this in a lot of unique ways, I’ll try one that I think is easy enough to allow you to add or remove buttons without change many things

// This represent my upgrade cost, I'll send this to the resource manager, and the resource manager will see if can pay for this upgrade or not
[Serializable] // Allow me to customize the cost for each upgrade in inspector
public class ResourcesCost
{
    public int iron = 0;
    public int food = 0;
    public int wood = 0;
}

// This will represent our base class for upgrade buttons
public abstract class UpgradeButton : MonoBehaviour
{
    // Our button reference
    protected Button button;
   
    // This isn't exactly the best approach, cause you will need to remember to call base.Awake() on child.
        // Isn't a big problem because if you forget errors will prompt making you correct yourself 
    protected virtual void Awake()
    {
        // Here we will get and cache the button component
        button = GetComponent<Button>();
       
        // We will add our Upgrade abstract code to the button
        button.onClick.AddListener(Upgrade);
    }

    // This will be implemented for each of our buttons
    protected abstract void Upgrade();
}

// Now we will branch our UpgradeButton to specific behaviours, I'll do for food and you will duplicate it for each case
// I don't know exactly what you mean by upgrade, so I'll suppose that you have an field that generate food in your manager, and upgrade food will increase this value in the manager
public class UpgradeFood : UpgradeButton
{
    [Header("Status")]
    public ResourcesCost resourceCost;
       
    private ResourcesManager resourcesManager;

    protected override void Awake()
    {
        base.Awake();

        resourcesManager = GameObject
            .FindObjectWithTag("ResourceManager")
            .GetComponent<ResourceManager>();
    }

    // Now when you press the button, this upgrade will be called
    protected override void Upgrade()
    {
        if (!resourcesManager.CanUpgrade(resourceCost))
            return;
       
        resourcesManager.IncreaseFoodGainRate += 12; // Some arbitrary number
    }
}

// But let's say, for example, that one of your food buttons is an especial one that will have unique upgrade behavior,
// You can inherit from FoodUpgrade and just rewrite Upgrade, or create another UpgradeButton child directly
public class SpecialUpgradeFood : UpgradeFood
{
    protected override void Upgrade()
    {
        if (!resourceCost.CanUpgrade(resourceCost))
            return;
       
        // Do something unique for this button
    }
}

PS: I’ve made this only with the IDE, so it will probably have typos, but the idea is this.

Also, in a real scenario, I would probably add some actions to communicate between success and failure to upgrade something, but you can add this on the run. I hope this helps

N

Good morning.
I’ve had a lot of work and I haven’t had much time to program, I’ve been trying, but it doesn’t work for me. In addition to so much testing, I have a problem, I have removed my list that went through the mines putting the function with parameters that names the class “FuncionamientoMinas”, but now I can’t instantiate it or name it. Can anyone help me.
Thanks.

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

public  class CaracteristicasMinas: MonoBehaviour
{
    public Mejora mejora;
    public GameObject panelSeguro;
    public TextMeshProUGUI panelMejora;
    public string strin;

    public enum KindOfMinas
    {
        Store,
        Resources,
        Etc
    }

    [System.Serializable]
    public class Minas
    {
        public Sprite image;
        public string nombre;
        public Button button;
        public float produccion, almacen, multi;
        public bool mejora;
        public int habitantes, mejorasuma, nMejora;
        public TextMeshProUGUI panelTexto;
        public InputField cntHabitantes;
        public List<Mejora> mejorarMina = new List<Mejora>();
        public GameObject panelInfo;

        // public Button button2;
        public KindOfMinas kindOfMinas;



    }

    [System.Serializable]
    public class Mejora
    {
        public List<int> materiales;

    }
    public List<Minas> recursos = new List<Minas>();

    // You will call this on your FuncionamentoMinas method
    public void AddBehaviourToButton(Minas minas)
    {
        var kind = minas.kindOfMinas;
        var button = minas.button;
        var imagen = minas.image;

        switch (kind)
        {
            case KindOfMinas.Store:
                button.onClick.AddListener(() => print("funcionaenserio"));
                {

                }
                break;

            case KindOfMinas.Resources:
                // You can also subdivide into methods
                button.onClick.AddListener(OnClickInResourcesButton);
                break;

            case KindOfMinas.Etc:
                // repeat
                break;

            default:
                throw new System.ArgumentOutOfRangeException();
        }
    }

    private void OnClickInResourcesButton()
    {
        //recursos[i].mejora = true;
        print("hola funciona");
    }
    IEnumerator Start()
    {
        while (true)
        {
            FuncionamientoMinas();
            yield return new WaitForSeconds(1f);
          
        }
    }

    /*private void FuncionamientoMinas()
    {
        throw new NotImplementedException();
    }*/

    public void FuncionamientoMinas(Minas minas) // no funciona el invoke con metodos con parametros, mirar historial del 20/10 últimas páginas de la noche y ver los foros y seguir
    {
        float produccion = minas.produccion;
        float habitantes = minas.habitantes;
        float multi = minas.multi;
        var cntHabitantes = minas.cntHabitantes;
        var panelTexto = minas.panelTexto;
        float almacen = minas.almacen;






        habitantes = int.Parse(cntHabitantes.text);
        produccion = produccion + (multi * habitantes);
        produccion = Mathf.Ceil(produccion);
        panelTexto.text = produccion.ToString();

        if (produccion > 10000 && produccion < 1000000)
        {

            panelTexto.text = Mathf.Round(produccion / 1000).ToString() + "K";

        }
        if (produccion > 1000000)
        {

            panelTexto.text = Mathf.Round(produccion / 1000000).ToString() + "M";

        }
        if (almacen < produccion)
        {
            produccion = almacen;
            panelTexto.color = Color.red;
            panelTexto.text = produccion.ToString();

        }

How to report your problem productively in the Unity3D forums:

http://plbm.com/?p=220

If you don’t know what the code is doing at runtime, nobody here does either. Here is how to find out:

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

Good afternoon.
I think it is not because of Debug.Log, many times I have used it, to see if it works, but the problem is that the method “FuncionamientoMinas”, does not work, it gives me error, because having parameters that I have to pass a class in that parameter, I do not know how to instantiate it in the IEnumerator, it is seen in my comment 7, although the error is not reflected.
I hope I have explained it well. Thanks to all of you who help me.

You could make the Minas class static and avoid the need for any references (although static classes are always kept in memory so not the best solution)

    public static class Minas
    {
        public static int number = 10;
    }
private void SomeFunction()
{
    Debug.Log(Minas.number);
}

Otherwise you need to provide a reference.

using UnityEngine;

public class CaracteristicasMinas : MonoBehaviour
{
    Minas minas = new Minas();

    public class Minas
    {
        public int number = 14;
    }

    public void FuncionamientoMinas(Minas minas)
    {
        Debug.Log(minas.number);
    }

    private void Update()
    {
        FuncionamientoMinas(minas);
    }
}

Good afternoon.
I have done so, but having “Mines” in a list does not seem to go through it, or at least I think it does.
In console I get; Object reference not set to an instance of an object.
That’s why I think it is not referenced. My idea was that, not to have to go through the list, so that I don’t have a constant “for”.
I leave you the script of how it has been and thanks again for the help, to see if we can give you some solution. Thanks from the bottom of my heart.

public  class CaracteristicasMinas: MonoBehaviour
{
    Minas minas = new Minas();
    public Mejora mejora;
    public GameObject panelSeguro;
    public TextMeshProUGUI panelMejora;
    public string strin;

 

    [System.Serializable]
    public class Minas
    {
      
        public Sprite image;
        public string nombre;
        public Button button;
        public float produccion, almacen, multi;
        public bool mejora;
        public int habitantes, mejorasuma, nMejora;
        public TextMeshProUGUI panelTexto;
        public InputField cntHabitantes;
        public List<Mejora> mejorarMina = new List<Mejora>();
        public GameObject panelInfo;

    

    }

    [System.Serializable]
    public class Mejora
    {
        public List<int> materiales;

    }
    public List<Minas> recursos = new List<Minas>();

  

  
    }

    /*private void FuncionamientoMinas()
    {
        throw new NotImplementedException();
    }*/
  
    public void FuncionamientoMinas(Minas minas)
        print("entra");
        Debug.Log(minas.produccion);
        minas.habitantes = int.Parse(minas.cntHabitantes.text);
        minas.produccion = minas.produccion + (minas.multi * minas.habitantes);
        minas.produccion = Mathf.Ceil(minas.produccion);
        minas.panelTexto.text = minas.produccion.ToString();

        if (minas.produccion > 10000 && minas.produccion < 1000000)
        {

            minas.panelTexto.text = Mathf.Round(minas.produccion / 1000).ToString() + "K";

        }
        if (minas.produccion > 1000000)
        {

            minas.panelTexto.text = Mathf.Round(minas.produccion / 1000000).ToString() + "M";

        }
        if (minas.almacen < minas.produccion)
        {
            minas.produccion = minas.almacen;
            minas.panelTexto.color = Color.red;
            minas.panelTexto.text = minas.produccion.ToString();

        }