I am new to Unity and have been experimenting with dice rolling. I came across a set of tutorials which allowed me to create a 3d die (the die uses Rigidbody and Mesh Collider) and script it to roll on the press of a spacebar as follows:
Dice.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded;
bool thrown;
Vector3 initPosition;
public int diceValue;
public DiceSide[] diceSides;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
initPosition = transform.position;
rb.useGravity = false;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space)){
RollDice();
}
if (rb.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
rb.useGravity = false;
rb.isKinematic = true;
SideValueCheck();
}
else if (rb.IsSleeping() && hasLanded && diceValue == 0)
{
RollAgain();
}
}
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 = initPosition;
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(diceValue + " has been rolled!");
}
}
}
}
DiceSide.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiceSide : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
bool onGround;
public int sideValue;
private void OnTriggerStay(Collider col)
{
if(col.tag == "Ground")
{
onGround = true;
}
}
private void OnTriggerExit(Collider col)
{
if(col.tag == "Ground")
{
onGround = true;
}
}
public bool OnGround()
{
return onGround;
}
}
Each side of the die has a sphere to which DiceSide.cs is attached containing the side value opposite of the given face and Dice.cs is attached to the main dicecube itself.
All of the above is working just fine.
The problem I am facing is in adapting this to instead of pressing a key (in this case, spacebar) I want to roll the dice after clicking on a button.
To do so, I modified my Dice.cs code as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded;
bool thrown;
Vector3 initPosition;
public int diceValue;
public DiceSide[] diceSides;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
initPosition = transform.position;
rb.useGravity = false;
}
// Update is called once per frame
void Update()
{
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 = initPosition;
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(diceValue + " has been rolled!");
}
}
}
}
But when I drag the Dice.cs script onto the button as follows:
I get an error from Unity saying
MissingComponentException: There is no 'Rigidbody' attached to the "Button-RollDice" game object, but a script is trying to access it. You probably need to add a Rigidbody to the game object "Button-RollDice". Or your script needs to check if the component is attached before using it. Dice.Update () (at Assets/Dice.cs:35)
I know enough to understand that the code is looking for Rigidbody on the button instead of the die but I don't know how to modify my code to get it to act on the die as expected.
Thank you.
The dice script would still need to be on the Dice object. You would need a intermediary script with an OnClick event to assign to the button with a reference to the Dice script which could then call RollDice.
Something like:
public class ButtonHandler : MonoBehavior
{
// Assign dice game object to script in editor
public GameObject DiceGameObject;
private Dice dice;
void Awake()
{
// Retrieve the script from the gameobject
dice = diceGameObject.GetComponent<Dice>();
}
public void RollDiceOnClick()
{
// Call the roll dice method
dice.RollDice();
}
}
You would assign the RollDiceOnClick() method to the OnClick event of the button (instead of RollDice).
Then you would drag the dice gameobject onto the DiceGameObject property and everything should link up