Home > Software design >  How to call a method before another from 2 different scripts?
How to call a method before another from 2 different scripts?

Time:04-01

I am trying to call a method from the script Dice and after that method is executed the value of the variable diceValue will change. This is the value that I want to take and use in the method from the script Levizja.

Levizja.cs

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

public class Levizja : MonoBehaviour
{

    public Transform[] lojtaret;
    public GameObject zari;
    private int numer = 0;
    public Dice vleraEzarit;

void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            GetComponent<Rigidbody>().AddForceAtPosition(new Vector3(Random.Range(0, 500), Random.Range(0, 500) * 10, Random.Range(0, 500)), new Vector3(0, 0, 0), ForceMode.Force);
            Debug.Log("U hodh zari me numer: "   Dice.getValue());
        }
    }
}

Dice.cs

using System.Collections.Generic;
using UnityEngine;

public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded, thrown;
Vector3 initPosition;
[SerializeField] private static int diceValue;
private static int numriLojtareve;

    //public int diceValue;
    
    public DiceSides[] diceSides;
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Reset();
            RollDice();
        }
        if (rb.IsSleeping() && !hasLanded && thrown)
        {
            hasLanded = true;
            rb.useGravity = false;
            rb.isKinematic = true;
            SideValueCheck();
        }
        else if (rb.IsSleeping() && hasLanded && diceValue == 0)
            RollAgain();
    }
    
    void SideValueCheck()
    {
        diceValue = 0;
        foreach(DiceSides side in diceSides)
        {
            if (side.OnGround())
            {
                diceValue = side.getValue();
                Debug.Log(diceValue   " has been rolled!");
            }
        }
    }
    public static int getValue()
    {
        return diceValue;
    }

}

Some of the methods are not included just to address only the issue.

I want to execute the Update method in Dice.cs which will call SideValueCheck method. This way the variable diceValue will be updated. After that I want the Update method in Levizja.cs to execute this way the new value will be stored there. What happens is the first time I get the value 0 and the next run I get the last value that dice had. So if first time it landed 3 it shows 0. Next time it lands 2 it shows 3 and so on.

CodePudding user response:

Ideally the diceValue should be returned by the method and Dice should not have a state. If you absolutely need Dice to have a state then it should not be static.

CodePudding user response:

You could adjust this in the Script Execution Order and force a certain order of the executions of the same event message type (Update in this case).

However, this won't be enough. You rather want to wait until the dice has a result.


So before/instead of touching the execution order I would rather rethink the code structure and do something else. E.g. why do both your scripts need to check the user input individually?

Rather have one script call the methods of the other one event based. There is also no reason to have things static here as you already have a reference to an instance of a Dice anyway in vleraEzarit

public class Dice : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private Rigidbody _rigidbody;
    [SerializeField] private DiceSides[] diceSides;

    [Header("Debug")]
    [SerializeField] private int diceValue;

    private bool hasLanded, thrown;
    private Vector3 initPosition;
    
    private int numriLojtareve;

    // if you still also want to also provide a read-only access
    // to the current value
    public int DiceValue => diceValue;  

    // Event to be invoked everytime there is a new valid dice result
    public event Action<int> OnHasResult;

    private void Awake()
    {
        if(!_rigidbody)
        {
            _rigidbody = GetComponent<Rigidbody>();
        }
    }
    
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Reset();
            RollDice();
        }

        if (_rigidbody.IsSleeping() && !hasLanded && thrown)
        {
            hasLanded = true;
            _rigidbody.useGravity = false;
            _rigidbody.isKinematic = true;
            SideValueCheck();
        }
        else if (_rigidbody.IsSleeping() && hasLanded && diceValue == 0)
        {
            RollAgain();
        }
    }
    
    void SideValueCheck()
    {
        foreach(DiceSides side in diceSides)
        {
            if (side.OnGround())
            {
                diceValue = side.getValue();
                Debug.Log(diceValue   " has been rolled!");
 
                // Invoke the event and whoever is listening to it will be informed
                OnHasResult?.Invoke(diceValue);

                // also I would return here since i makes no sense to continue
                // checking the other sides if you already have a result
                return;
            }
        }

        // I would move this here
        // In my eyes it is clearer now that this is the fallback case
        // and only happening if nothing in the loop matches
        diceValue = 0;
    }
}

And then make your Levizja listen to this event and only act once it is invoked like e.g.

public class Levizja : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private Rigidbody _rigidbody;
    [SerializeField] private Transform[] lojtaret;
    [SerializeField] private GameObject zari;
    [SerializeField] private Dice vleraEzarit;

    private int numer = 0;

    private void Awake()
    {
        if(!_rigidbody)
        {
            _rigidbody = GetComponent<Rigidbody>();
        }

        // Attach a callback/listener to the event 
        // just out of a habit I usually remove it first to make sure it can definitely only be added once
        vleraEzarit.OnHasResult -= HandleDiceResult;
        vleraEzarit.OnHasResult  = HandleDiceResult;
    }

    private void OnDestroy()
    {
        // make sure to remove callbacks once not needed anymore 
        // to avoid exceptions
        vleraEzarit.OnHasResult -= HandleDiceResult;
    }

    // This is called ONCE everytime the dice has found a new result
    private void HandleDiceResult(int diceValue)
    {
        _rigidbody.AddForceAtPosition(new Vector3(Random.Range(0, 500), Random.Range(0, 500) * 10, Random.Range(0, 500)), new Vector3(0, 0, 0), ForceMode.Force);
        Debug.Log("U hodh zari me numer: "   Dice.getValue());
    }
}
  • Related