Multiple dice throw mechanic C# Unity - c#

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

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

Related

Unity C# how should I call the function and values from different script files?

I am new to Unity and c#. I am trying to create a dice game that rolls two dices and the total values of the two dice faces is added to the player's score. I am intending the game to continue until the player rolls two 1's or the total score reached or exceeds 50, and at the end display win or lose message and the score. I somehow managed to implement most of it. However, I can't manage to update score after rolling the dice. I tried on my own to do so, but now it's keep adding the dice rolls without break and prints win message right away.
This is the Dice code
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded;
bool thrown;
Vector3 initPosition; //Initial Position
public int diceValue;
public DiceSide[] diceSides;
void Start()
{
rb = GetComponent<Rigidbody>();
initPosition = transform.position;
rb.useGravity = false;
}
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!");
}
}
}
}
and this is the sides code(just in case. not really relevant to the problem I am having but I am still attaching just in case)
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;
}
}
and here's the code I am having the hardest time with...
public class Check : MonoBehaviour
{
public GameObject DiceGameObject1;
public GameObject DiceGameObject2;
private Dice dice;
private Dice dice2;
public int dicevalue1;
public int dicevalue2;
private int score;
private int totalScore;
public TMP_Text Score;
public TMP_Text TotalScore;
public TMP_Text Win;
public TMP_Text Lose;
public TMP_Text Player;
void Start()
{
totalScore = 0;
score = 0;
Win.text = "";
Lose.text = "";
}
void Awake()
{
dice = DiceGameObject1.GetComponent<Dice>();
dice2 = DiceGameObject2.GetComponent<Dice>();
}
void Update()
{
UpdateScore();
}
void SetScoreText()
{
if (dicevalue1 == 1 && dicevalue2 == 1)
{
Lose.text = "You Lose:(";
}
else if (totalScore >= 50)
{
Win.text = "You win!";
}
else
{
TotalScore.text = totalScore.ToString();
}
}
public void UpdateScore()
{
dicevalue1 = dice.diceValue;
dicevalue2 = dice2.diceValue;
score = dicevalue1 + dicevalue2;
Debug.Log(dicevalue1 + dicevalue2);
// how do I import diceValue variables from Dice class and use the values here?
totalScore += score;
SetScoreText();
}
}
So I first had problem using the diceValue variable(value) from Dice script at Check script, but I somehow managed to. I am quite not understanding how, but I tried GetComponent from Dice file to make it somehow work. I was wondering if I should do that with UpdateScore method as well, but I am stuck...
Eventually I am trying to make this a turn-based two-player game, where player take turns. So far I barely managed it to work as one player game...
Please help!!
You are calling UpdateScore() in Update(). Update() is called every frame, so when your SideValueCheck() has evaluated a dice number, the score will rise very quickly to 50. What you need is a condition to query when the dice numbers have been evaluated such that you update the score only once. You could add in your Check class a new flag bool scoreUpdated = false; and change UpdateScore() to
public void UpdateScore()
{
dicevalue1 = dice.diceValue;
dicevalue2 = dice2.diceValue;
// only update if not done already and the dice have meaningful side values
if (!scoreUpdated && dicevalue1 != 0 && dicevalue2 != 0) {
score = dicevalue1 + dicevalue2;
Debug.Log(dicevalue1 + dicevalue2);
// how do I import diceValue variables from Dice class and use the values here?
// (Edit: you did that already)
totalScore += score;
scoreUpdated = true; // track the score update
SetScoreText();
}
}
Now, if you do a new dice roll, you need to first reset the dice value of the rolling dice. Reset() would be edited to:
void Reset()
{
transform.position = initPosition;
thrown = false;
hasLanded = false;
diceValue = 0; // the dice is rolling again, so no valid side value
rb.useGravity = false;
//rb.isKinematic = false;
}
and of course you need to reset the score tracking which you could do maybe in Update() of Dice class, because you located the input code there:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
RollDice();
check.scoreUpdated = false;
}
How to get check? I dont know where you attached it, but you need to get a reference to that game object and then get the script component on it with Check check = go.GetComponent<Check>(); just like you did with the dice variables.

How to See The Position Of An Index. Like 1-15 Not Coordinates

To preface this I really don't understand how well to phrase this other than the title. I'm trying to see which position the Index is on to test whether I can go back or if that will go out of the range. For example something like this. Also I think the index starts at one but I don't know for sure. By the way the spacing in the beginning might be a little confusing but that got moved because of stack overflow not recognizing it as code.
if (Index.Position == 0)
{
CanGoBack = false;
}
else
{
CanGoBack = true;
}
Here is the actual written code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Dialog : MonoBehaviour
{
public TextMeshProUGUI textDisplay;
public string[] sentences;
private int index;
public float typingSpeed;
public GameObject player;
public GameObject Dialog_Manager;
public bool NextLine = true;
public bool CanGoBack = false;
IEnumerator Type()
{
foreach(char letter in sentences[index].ToCharArray())
{
textDisplay.text += letter;
yield return new WaitForSeconds(typingSpeed);
}
}
IEnumerator Wait()
{
yield return new WaitForSeconds(2.5f);
index++;
textDisplay.text = "";
NextLine = true;
CanGoBack = true;
}
void Start()
{
StartCoroutine(Type());
}
public void Play()
{
player.SetActive(true);
Dialog_Manager.SetActive(false);
}
public void LastSentence()
{
if(index < sentences.Length - 1)
{
index--;
textDisplay.text = "";
StartCoroutine(Type());
}
else
{
textDisplay.text = "";
}
}
public void NextSentence()
{
if(index < sentences.Length - 1)
{
index++;
textDisplay.text = "";
StartCoroutine(Type())
}
else
{
textDisplay.text = "";
}
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
NextSentence();
}
if(Input.GetMouseButtonDown(1))
{
LastSentence();
}
}
}
int position = sentences.IndexOf("your string");

UNITY3D c# Teleport object on certain amount of points

I encountered a problem with this code. The "Killer" teleports to the location and immediately goes back to the starting position. I have 7 locations for the "Killer" to teleport to (around the player). Whats wrong with this code? (I am a newbie) If possible could you add a sound to every point collected? example. When I get 1 points a sound/audio file plays. Thanks for answers!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FpsScoreScript : MonoBehaviour
{
public int points;
bool isTeleported1 = false;
bool isTeleported2 = false;
bool isTeleported3 = false;
bool isTeleported4 = false;
bool isTeleported5 = false;
bool isTeleported6 = false;
bool isTeleported7 = false;
bool isTeleported8 = false;
public GameObject Killer;
public GameObject Player;
public Transform Destination;
public Transform KillerDestFarBack;
public Transform KillerDestCloseBack;
public Transform KillerDestRight;
public Transform KillerDestLeft;
public Transform KillerDestRightFront;
public Transform KillerDestLeftFront;
public Transform KillerDestFront;
public void Update()
{
if (points == 1 && !isTeleported1)
{
Teleport1();
}
if (points == 2 && !isTeleported2)
{
Teleport2();
}
if (points == 3 && !isTeleported3)
{
Teleport3();
}
if (points == 4 && !isTeleported4)
{
Teleport4();
}
if (points == 5 && !isTeleported5)
{
Teleport5();
}
if (points == 6 && !isTeleported6)
{
Teleport6();
}
if (points == 7 && !isTeleported7)
{
Teleport7();
}
if (points == 8 && !isTeleported8)
{
Teleport8();
}
}
void Teleport1()
{
isTeleported1 = true;
Killer.transform.position = KillerDestFront.transform.position;
}
void Teleport2()
{
isTeleported2 = true;
Killer.transform.position = KillerDestRightFront.transform.position;
}
void Teleport3()
{
isTeleported3 = true;
Killer.transform.position = KillerDestLeftFront.transform.position;
}
void Teleport4()
{
isTeleported4 = true;
Killer.transform.position = KillerDestRight.transform.position;
}
void Teleport5()
{
isTeleported5 = true;
Killer.transform.position = KillerDestLeft.transform.position;
}
void Teleport6()
{
isTeleported6 = true;
Killer.transform.position = KillerDestFarBack.transform.position;
}
void Teleport7()
{
isTeleported7 = true;
Killer.transform.position = KillerDestCloseBack.transform.position;
}
void Teleport8()
{
isTeleported8 = true;
Player.transform.position = Destination.transform.position;
}
}
Instead of using many Transform vars, I would prefer using a transform array, also I have used an array of bools has teleported. I would prefer you to follow some tutorials or get a book. Add a collider to your player and the coin.
[SerializeField] Transform[] teleportPoints;
[SerializeField]Transform coin;
bool[] hasTeleported;
AudioSource source;
int points = 0 ;
void Awake()
{
source = GetComponent<AudioSource>();
hasTeleported = new bool[teleportPoints.Length];
}
private void Update()
{
for(int i = 0; i <= teleportPoints.Length; i++)
{
if (points == i && !hasTeleported[i])
{
Teleport(i);
}
}
}
void Teleport(int index)
{
transform.position = teleportPoints[index].position;
hasTeleported[index] = true;
}
void IncreasePoints()
{
source.Play();
points++;
}
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject == coin.gameObject)
{
IncreasePoints();
}
}

Instantiate inside nested for loops does not do what it is intended to

I have been working on a minecraft clone game in Unity 2017.3 and C# and i started all right but when i worked on the super flat world generation system there began a problem. Inside the World mono behaviour, in the Generate void, there are three nested for loops which should generate a new dirt block on every possible position between 0 and 5
But it only makes a line of dirt block stretching along the Z axis.
Here is the code for PlaceableItem(attached to dirtPrefab) and World(attached to World)
Placeable Item class
using UnityEngine;
using System.Collections;
public class PlaceableItem : MonoBehaviour
{
public string nameInInventory = "Unnamed Block";
public int maxStack = 64;
public bool destructible = true;
public bool explodesOnX = false;
public bool abidesGravity = false;
public bool isContainer = false;
public bool canBeSleptOn = false;
public bool dropsSelf = false;
private Rigidbody rb;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (abidesGravity) {
rb.useGravity = true;
} else {
rb.useGravity = false;
}
}
private void OnDestroy()
{
if (dropsSelf) {
//Drop this gameObject
}
}
public void Explode() {
if (!explodesOnX)
return;
}
public void OnMouseDown()
{
if (isContainer && !canBeSleptOn) {
//Open the container inventory
} else if (canBeSleptOn && !isContainer) {
//Make the character sleep on the item
}
}
private void OnMouseOver()
{
if (Input.GetMouseButtonDown(1) && destructible) {
Destroy(gameObject);
}
}
}
World class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class World : MonoBehaviour {
public GameObject dirtPrefab;
public bool generateAutomatically = true;
// Use this for initialization
void Start () {
if (generateAutomatically) {
Generate();
}
}
// Update is called once per frame
void Update () {
}
void Generate() {
for (int x = 0; x <= 5; x++) {
for (int y = 0; y <= 5; y++) {
for (int z = 0; z <= 5; z++) {
Instantiate(dirtPrefab, new Vector3(x, y, z), Quaternion.identity);
}
}
}
}
void RemoveAllBlocks() {
foreach (PlaceableItem placeableItem in GetComponentsInChildren<PlaceableItem>()) {
Destroy(placeableItem.gameObject);
}
}
}
Thanks in advance, fellow developers!
Hope this is not a stupid question!
[SOLVED] I realised i had a recttransform somehow attached to my dirt prefab. Removing it and adding a transform instead helped.

Interactable button UNITY

I'm trying to make a simple condition:
If the value I have, is less than the price it costs the item, the button is disabled.
If the value I have, is greater than or equal to the price it costs the item the button is enabled and I can buy it.
But when I test, I have some problems.
First, if I have less than the item cost the button is enabled, and only when I click on it it is when it disables.
Second, if I have less than the item cost and I click on it it disables, but if I get enough to purchase the item, the button is not enabled again.
How do I to be checked these variables all the time? If I have enough the button is enabled if you do not have it disables.
Bellow my scrip:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class BuySkin : MonoBehaviour {
public int price;
public Button buyBee1;
void OnEnable ()
{
//Register Button Events
buyBee1.onClick.AddListener (() => buySkin (buyBee1));
}
public void buySkin(Button button)
{
if (BeeCoinScore.coin >= price) {
BeeCoinScore.coin -= price;
buyBee1.interactable = false;
}
if (BeeCoinScore.coin < price) {
buyBee1.interactable = false;
}
}
void OnDisable ()
{
//Un-Register Button Events
buyBee1.onClick.RemoveAllListeners ();
}
}
Try this out with some prefabs!
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class GameController : MonoBehaviour
{
public int coins;
private int spherePrice = 100, cubePrice = 50;
public GameObject player;
public GameObject[] availablePrefabs;
public List<GameObject> mySkins;
public Button btnSphere, btnCube;
public Text txtSphere, txtCube;
void Start ()
{
string serializedMySkins = PlayerPrefs.GetString ("skins", "");
string serializedPlayer = PlayerPrefs.GetString ("player", "");
// skins desserialization
if (serializedMySkins == "")
mySkins = new List<GameObject> ();
else {
var a = serializedMySkins.Split (',');
for (int i = 0; i < a.Length; i++) {
if (a [i] == "Sphere") {
mySkins.Add (availablePrefabs [0]);
}
if (a [i] == "Cube") {
mySkins.Add (availablePrefabs [1]);
}
}
}
// player desserialization
if (serializedPlayer != "") {
if (serializedPlayer == "Sphere") {
player = availablePrefabs [0];
}
if (serializedPlayer == "Cube") {
player = availablePrefabs [1];
}
} else {
player = mySkins [0];
}
coins = PlayerPrefs.GetInt ("coins", 0);
coins = 1000;
}
void Update ()
{
if (mySkins.Contains (availablePrefabs [0])) {
txtSphere.text = "Usar esfera";
} else {
btnSphere.interactable = coins >= spherePrice;
}
if (mySkins.Contains (availablePrefabs [1])) {
txtCube.text = "Usar cubo";
} else {
btnCube.interactable = coins >= cubePrice;
}
}
public void play ()
{
player = (GameObject)Instantiate (player, new Vector2 (0, 0), Quaternion.identity);
}
public void verifySkin (GameObject skinPrefab)
{
if (mySkins.Contains (skinPrefab)) {
useSkin (skinPrefab);
} else if (coins >= priceOf (skinPrefab)) {
buySkin (skinPrefab, priceOf (skinPrefab));
}
}
public void buySkin (GameObject skinPrefab, int price)
{
mySkins.Add (skinPrefab);
coins -= price;
string skinsHash = "";
for (int i = 0; i < mySkins.Count; i++) {
skinsHash += mySkins [i].name + ",";
}
Debug.Log (skinsHash);
PlayerPrefs.SetInt ("coins", coins);
PlayerPrefs.SetString ("skins", skinsHash);
PlayerPrefs.Save ();
}
public void useSkin (GameObject skinPrefab)
{
player = skinPrefab;
PlayerPrefs.SetString ("player", player.name);
PlayerPrefs.Save ();
}
private int priceOf (GameObject skinPrefab)
{
if (skinPrefab == availablePrefabs [0])
return spherePrice;
else if (skinPrefab == availablePrefabs [1])
return cubePrice;
else
return 0;
}
}
OnEnable()is called when the object becomes enabled and active.
you need Update() as it is getting called every frame it will check whether your value is less than or greater than price of item.You may also try like this.
// I think that you are making an buymenu, so you can disable and enable your menu with ui button and check money you have
using System.Collections;
using UnityEngine.UI;
public class BuySkin : MonoBehaviour
{
public int price;
public static int money;// money you have
public Button thisbuyBee1;
public void buychkr()
{
if(price>= money)
{
thisbuyBee1.interactable = false;
}
else
{
thisbuyBee1.interactable = true;
}
}
void Update()
{
buychkr();
}
}

Categories