简体   繁体   中英

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

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 eg

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--;
}

在此处输入图像描述

i do not know unity... so if this is off base with regards to that then i will gladly delete.

public class DiceController : MonoBehaviour
{
    public List<Dice> dices;

    void DiceController()
    {
        dices = new list<Dice>();
        dices.add(new Dice); //ensure always have at least 1 on start
    }

    public void Plus()
    {        
        dices.Add(new Dice);
    }

    //caller can decided, could be random which one get removed.
    //assume they all equal
    public void Minus(Dice dice){  
        dices.Remove(dice);
    }

    public void Throw()
    {
        foreach(Dice dice in dices){
            dice.RollDice();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM