Search code examples
c#unity-game-enginedice

Multiple dice throw mechanic C# Unity


I would like to istantiate multiple dices (you should be able to add and substract dices) and roll them.

For now I can roll a dice and get the readout in the console. My problem: I can't get multiple dice to work...

These are the scripts:

the dice controller:

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

public class DiceController : MonoBehaviour
{
    public Dice dice;
    public GameObject dicePre;

    public int count = 1;


void Update()
{   
    GameObject[] dices = GameObject.FindGameObjectsWithTag("Dice");
    if(count - 1 == dices.Length){
        for (int i = 0; i < count; i++)
            {
                Instantiate(dicePre, new Vector3(i * 1.1F, 0, 0), Quaternion.identity);
            }
    }
    else if(count -1 < dices.Length){
        return;
    }
}

    public void Throw()
    {
        GameObject[] dices = GameObject.FindGameObjectsWithTag("Dice");
        foreach(GameObject dic in dices){
            dice = dic.GetComponent<Dice>();
            dice.RollDice();
        }
        
    }

    public void Plus(){        //add dice
        count++;
    }

    public void Minus(){       //substract dice
        count--;
    }
}

the dice sides:

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

public class DiceSide : MonoBehaviour
{
    bool onGround;
    public int sideValue;

    void OnTriggerStay(Collider col) {
        
        if(col.tag == "ground"){
            onGround = true;
        }
    }

    void OnTriggerExit(Collider col) {
        
        if(col.tag == "ground"){
            onGround = false;
        }
    }

    public bool OnGround(){
        return onGround;
    }

}

the main dice script:

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

public class Dice : MonoBehaviour
{
   Rigidbody rb;
   bool hasLanded;
   bool thrown;
   Vector3 initPos;

   public int diceValue;

   public DiceSide[] diceSides;
   
   private void Start(){
        rb = GetComponent<Rigidbody>();
        initPos = transform.position;
        rb.useGravity = false;    
   }


    private void Update(){
        if(Input.GetKeyDown(KeyCode.T)){
            RollDice();
        }

        if(rb.IsSleeping() && !hasLanded && thrown){
            hasLanded = true;
            rb.useGravity = false;
            rb.isKinematic = true;

            SideValueCheck();
        }
        else if(rb.IsSleeping() && hasLanded && diceValue == 0){
            RollAgain();
        }
    }

    public void RollDice(){

        if(!thrown && !hasLanded){
            thrown = true;
            rb.useGravity = true;
            rb.AddTorque(Random.Range(0,500), Random.Range(0,500), Random.Range(0,500));

        }
        else if(thrown && hasLanded){
            Reset();
        }
    }

    void Reset(){

        transform.position = initPos;
        thrown = false;
        hasLanded = false;
        rb.useGravity = false;
        rb.isKinematic = false;
    }

    void RollAgain(){
        Reset();
        thrown = true;
        rb.useGravity = true;
        rb.AddTorque(Random.Range(0,500), Random.Range(0,500), Random.Range(0,500));
    }

    void SideValueCheck(){
        diceValue = 0;
        foreach(DiceSide side in diceSides){
            
            if(side.OnGround()){
                diceValue = side.sideValue;
                Debug.Log("Eine " + diceValue + " wurde gewürfelt!");
            }
        }
    }

}

How can I get this to work? also here you can download the unitypackage with everything i got right now: https://workupload.com/file/7brN4gTCeLu


Solution

  • First as said I would directly make the prefab field of type

    public Dice dicePre;
    

    then I would not use FindGameObjectsWithTag all the time to get current instances.

    I would rather keep track of them in a List like e.g.

    public class Dice : MonoBehaviour
    {
        // every instance will add itself to this list
        private static List<Dice> instances = new List<Dice>();
    
        // public read only access
        public static ReadOnlyCollection<Dice> Instances => instances.AsReadOnly();
    
        // Add yourself to the instances
        private void Awake()
        {
            instances.Add(this);
        }
    
        // Remove yourself from the instances
        private void OnDestroy()
        {
            instances.Remove(this);
        }
    }
    

    So later you can simply use

    foreach(var dice in Dice.Instances)
    {
        dice.RollDice();
    }
    

    The main issue

    Then currently you are checking

    if(count - 1 == dices.Length)
    

    and if so you instantiate count dices.

    • So what if your dices is empty and your count is 3? -> nothing would happen

    • Or what if you already have 2 dices but count is 3 -> you spawn 3 dices and end up with 5!

    You would need to actually check the difference between the dices amount and count and either add or remove only the difference.

    In order to fix this I would not do this in Update but rather using a property like

    [SerializeField] private int _count;
    
    public int Count
    {
        get => _count;
        set
        {
            // Count can not be negative
            _count = Mathf.Max(0, value);
    
            // Now do something with this new value
    
            // check difference
            var dif = Dice.Instances.Count - _count;
    
            // if 0 -> nothing to do
            if(dif == 0)
            {
                return;
            }
    
            // if smaller then 0 -> need more dices
            if(dif < 0)
            {
                for(var i = dif; i < 0; i++)
                {
                    Instantiate(dicePre, Vector3.right * Dice.Instances.Count, Quaternion.identity);
                }
            }
            // dif bigger then 0 -> have to many dices
            else
            {
                for(var i = 0; i < dif; i++)
                {
                    DestroyImmediate(Dice.Instances[Dice.Instances.Count - 1]);
                }
            }
        }
    }
    
    [ContextMenu(nameof(Plus))]
    public void Plus()
    {      
        Count++;
    }
    
    [ContextMenu(nameof(Minus))]
    public void Minus()
    {  
        Count--;
    }
    

    enter image description here