Trying to roll a die and cannot set the value after it is first set.

This is my first post here and also my first game and attempt at learning C# scripting with Unity. I am loving it so far.

I’m making a 3D board game, and I have created the board and a die, and set up nodes for the player to move to given a set number of steps which is determined by rolling a die.

When I first roll the die, the player moves properly (Say, they roll a 5, and then can move 5 spaces.)

However, after the first roll, every other roll just makes the player continue to move the first number that was rolled, regardless of what the die lands on. (In this example 5 spaces.) I cannot figure out why the die’s value is not resetting. I will do my best to describe how I have the game setup so far.

Firstly, I have the movement controlled by a script called ‘SpaceController.cs’
The code for that is right here:

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

public class SpaceController : MonoBehaviour
{
    public Movement currentSpace; //The current space the player is on.
    int spacePosition; //The space position.
    public int steps; //The number of steps the player has to take on their turn.
    bool isMoving; //Whether the player is currently moving or not.
    public GameObject dice; //Referencing the die for later usage.
    void Update()
    {
        Dice diceValue = dice.GetComponent<Dice>(); //Getting the Dice script from the die game object.
        if(Input.GetKeyDown(KeyCode.W) && !isMoving) //If W is pressed and the player is not moving.
        {
            steps = diceValue.diceValue; //Set the steps to the value of the die.
            if (spacePosition + steps < currentSpace.childSpaceList.Count) //If the current position plus the number of steps is less than the finish
            {
                StartCoroutine(Move()); //Move the player.
            }
            else
            {
                Debug.Log("You have won!"); //Tell the player they have won the game.
            }
        }
    }
    IEnumerator Move()
    {
        if(isMoving) //Checks if the player is already moving.
        {
            yield break; //Stops the coroutine.
        }
        isMoving = true; //Sets the player to be moving.

        while(steps>0) //While the available steps is more than 0.
        {
            Vector3 nextSpace = currentSpace.childSpaceList[spacePosition + 1].position; //Sets the current position to be the next space.
            while(MoveToNextSpace(nextSpace)){yield return null;} //Checks if the next space is not the finish.

            yield return new WaitForSeconds(0.1f); //Wait for some time between moves.
            steps--; //Subtract the used step from the available ones.
            spacePosition++; //Add to the current space count.
        }
        isMoving = false; //Reset moving to be false.
        steps = 0; //Manually set the steps to 0 if not done so by running out of available steps.
    }

    bool MoveToNextSpace(Vector3 goal) //Checks if the next space is the finish line or not.
    {
        return goal != (transform.position = Vector3.MoveTowards(transform.position, goal, 10f * Time.deltaTime)); //Allows the player to move.
    }
}

I also have the die GameObject which has 6 sides, each of the sides has a sphere collider set as a trigger attached to it, to detect collisions with the floor of the box the die rolls inside of. When one side is down, the opposite side is up. Each sphere has the following ‘DiceSides.cs’ script attached to them, and the value of each side is set manually in the inspector.

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

public class DiceSides : MonoBehaviour
{
    bool onGround; //Is the side on the ground.
    public int sideValue; //The value of the side, set manually in the inspector.
    void OnTriggerStay(Collider col) //When continually colliding with a trigger.
    {
        if(col.tag == "RollBox") //If the trigger is tagged the RollBox.
        {
            onGround = true; //Set that the side is on the ground.
        }
    }

    void onTriggerExit(Collider col) //When no longer colliding with a trigger.
    {
        if(col.tag == "RollBox") //If the trigger we are not colliding with is tagged the RollBox.
        {
            onGround = false; //Set that the side is not on the ground.
        }
    }

    public bool OnGround() //A public Method to check if the side is on the ground.
    {
        return onGround; //Return whether the side is on the ground or not.
    }
}

And then I have the actual die itself, which has the logic of rolling the die attached to it, which grabs the value of the side that landed on the ground. The following script ‘Dice.cs’ is attached to it.

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

public class Dice : MonoBehaviour
{
    Rigidbody rb; //Access the rigidbody of the die.
    bool hasLanded; //Checks if the die has landed.
    bool thrown; //Checks if the die has been thrown.
    Vector3 initPosition; //Captures the initial position of the die.
    public int diceValue; //The face value the die lands on.
    public DiceSides[] diceSides; //The array of the sides of the die.

    void Start()
    {
        rb = GetComponent<Rigidbody>(); //Initializing the rigidbody component.
        initPosition = transform.position; //Sets the initial position to be the position the die is currently at.
        rb.useGravity = false; //Disables the gravity of the die to begin with.
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)) //If the Space bar is pressed.
        {
            Roll(); //Calls the Roll method.
        }

        if(rb.IsSleeping() && !hasLanded && thrown) //If the die's rigidbody is still and it has not landed but was thrown
        {
            hasLanded = true; //Set the die to has landed
            rb.useGravity = false; //Disables gravity for the die while it is on the ground.
            ValueCheck();
        }
        else if (rb.IsSleeping() && hasLanded && diceValue == 0) //If the die's rigidbody is still and has landed but the value is uncertain
        {
            RollAgain(); //Re-rolls the die.
        }
        else if (rb.IsSleeping() && hasLanded && diceValue != 0) //If the die's rigidbody is still and has landed but the value is certain
        {
            ValueCheck(); //Check the value of the die.
        }
    }

    void Roll() //Roll the die.
    {
        if(!thrown && !hasLanded) //If the die has not been thrown or landed already
        {
            thrown = true; //Set the die to thrown.
            rb.useGravity = true; //Enable the gravity of the die.
            rb.AddTorque(Random.Range(0,1500),Random.Range(0,1500),Random.Range(0,1500)); //Adds a random force and spin to the die.
        }
        else if(thrown && hasLanded) //If the die has been thrown and has successfully landed.
        {
            Reset(); //Reset the die.
        }
    }

    void Reset() //Reset the die.
    {
        diceValue = 0;
        transform.position = initPosition; //Reset the position to the original position of the die.
        thrown = false; //Reset the thrown value so the die is counted as not being thrown yet.
        hasLanded = false; //Reset the landed value so the die is not counted as landed yet.
        rb.useGravity = false; //Turn off the gravity until the next roll.
    }

    void RollAgain() //Re-roll the die.
    {
        Reset(); //Reset the die.
        thrown = true; //Set the die to be thrown.
        rb.useGravity = true; //Turn on gravity.
        rb.AddTorque(Random.Range(0,1500),Random.Range(0,1500),Random.Range(0,1500)); //Add the random force and spin to the die.
    }

    void ValueCheck() //Check for the face value of the die.
    {
        diceValue = 0; //Set the die value to 0 until the die is certain of the face value.
        foreach(DiceSides side in diceSides) //Run through the array of die sides.
        {
            if(side.OnGround()) //If the specified side is on the ground.
            {
                diceValue = side.sideValue; //Set the value to the opposite side that is on the ground.
            }
        }
    }
}

So, basically, I cannot for the life of me think of why the value is not changing after the first roll. I have watched in the inspector on the die gameobject and the value shows the first rolled value, then when re-rolling it goes back to 0 as it should, then when it completes the next roll, it goes back to the first value that was rolled, regardless of what was actually rolled. I have spent at least 45 minutes scouring this code to try to find the problem and I just cannot think of it.

Any help would be greatly appreciated and if you need me to elaborate on anything, please just let me know.
Thank you for your time.

If you need to post 160 lines of code to explain an issue of “my variable never changes after it’s been set once”, I’m inclined to suspect you haven’t done enough to narrow things down. You have a specific value that you know is wrong (the number of steps you move). You should be able to trace that backwards to see where it first becomes wrong (in this case, probably when it was supposed to be set to another value, but wasn’t) to get at least one step closer to the root cause.

Based on a skim, I notice you have both a Roll() function and a RollAgain() function, which seems like a huge red flag (why should the second roll be different from the first?). I also don’t know why you would need so many “else if” blocks in Dice.Update–the result of the roll should presumably be known as soon as you call ValueCheck on line 32 and I don’t know why you’d need to recheck the value every frame after that, or wait a frame before rerolling.

My guess would be that your DiceSides class is failing to reset the “onGround” variable for some reason, so it thinks it’s still touching the ground even after you’ve rerolled. If several sides ALL think they are on the ground, your ValueCheck() method will take the last one it sees (and won’t raise any alarm at the fact that several numbers are somehow all rolled at once).

1 Like

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 are the values of the variables involved? Are they initialized?

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

1 Like

Apologies, I posted all of the code just to make sure I wasn’t excluding anything contextually important.

The RollAgain function is called to re-roll the die if it lands on an edge, or the value is uncertain. Basically, I’m trying to make sure that if the die doesn’t land properly, that it will automatically re-roll to make sure that it does.

As for the onGround variable, I used Kurt-Dekker’s suggestions of Debug.Logs, and Antistone was correct in that it is not detecting when the collision is no longer happening. I cannot figure out why that’s the case, though. From what I can tell, it should be working.

Check your capitalization.

1 Like

You are a saint. I cannot believe that I missed capitalizing the on in OnCollisionExit. This has been resolved.