Generate a unique random number between 1 and 10? - c#

I want to generate a unique random digit between 1 and 10. If a number gets repeated then the script should skip the number and try for another. If there are no unique numbers left then a message should be generated. Any logic to get this concept implemented?
public int number;
void Update () {
if(Input.GetKeyDown(KeyCode.A))
{
number= Random.Range(1,10);
}
}
EDIT: It is not a duplicate of the link the user posted since I am trying to generate unique number and if a particular number is repeated, the script ignores it and tries to look for another number.

Setup a list with all the valid values at Awake
Get a random value from the list
Remove the value from the list to avoid duplicates
BONUS - You could also extend the class to set any min and max numbers
public class RandomGenerator : MonoBehaviour
{
public int minNumber = 1;
public int maxNumber = 10;
private List<int> _validNumbers;
public int number;
private void Awake()
{
_validNumbers = new List<int>();
for (int i = minNumber; i <= maxNumber; i++)
_validNumbers.Add(i);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
if (_validNumbers.Count == 0)
Debug.Log("No valid Numbers");
else
number = GetRandomNumber();
}
}
private int GetRandomNumber()
{
var nextIndex = Random.Range(0, _validNumbers.Count - 1);
var result = _validNumbers[nextIndex];
_validNumbers.RemoveAt(nextIndex);
return result;
}
}
EDIT AFTER COMMENTS:
This question is very similar to this other question. But Unity.Random is different than System.Random.
The answers offered in the other question work here too. But we have more choices here.

As guys suggested in the comments, shuffling might work:
public class Shuffler
{
private readonly Queue<int> _queue;
public Shuffler(int max)
{
_queue = new Queue<int>(Enumerable.Range(1, max).OrderBy(x => UnityEngine.Random.value));
}
public bool TryGetNext(out int item)
{
if(_queue.Count == 0)
{
item = -1;
return false;
}
item = _queue.Dequeue();
return true;
}
}
For the sake of completenes, adding MonoBehaviour, e.g.
public class RandomNumber : MonoBehaviour
{
private Shuffler _shuffler;
public int number;
private void Awake()
{
_shuffler = new Shuffler(10)
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
if(!_shuffler.TryGetNext(out number))
{
Debug.Log("No valid Numbers");
}
}
}
}

Related

the seconds button not work well in unity?

I use two-button to set the index value, once I click it gave value to the index.
public class tabbar : MonoBehaviour
{
private int index = 0;
public int buttonindex;
public GameObject[] pannels;
// Update is called once per frame
void Update()
{
for (int i = 0; i < pannels.Length; i++)
{
if (i == index)
{
pannels[i].SetActive(true);
}
else
{
pannels[i].SetActive(false);
}
}
}
public void IWasClicked()
{
index = buttonindex;
}
}
the first one is ok to set 0,but the second one no work??
any ideas?
Try this and don't forget to update your buttonIndex:
public class tabbar : MonoBehaviour
{
public int buttonIndex;
public GameObject[] pannels;
private void UpdatePannels(int index)
{
for (int i = 0; i < pannels.Length; i++)
{
pannels[i].SetActive(i == index);
}
}
public void IWasClicked()
{
UpdatePannels(buttonIndex);
}
}

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

CustomEditor Unity: chosing enums at once + showing array

I'm trying to do a thing on Unity and got caught up;
This may be a bit complex, but see if you can help me:
What I got and what I want (image)
This is the script I got now
public class ClasseTest : MonoBehaviour{
public enum TiposDeMissoes
{
TipoMatarGente,
TipoPegarItem,
};
public TiposDeMissoes TiposMissoes;
[Serializable]
public class MatarGente
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] InimigosPraMatar;
}
[Serializable]
public class PegarItem
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] ItemsEntregar;
}
[Serializable]
public class Missoes
{
public TiposDeMissoes TiposMissoes;
public PegarItem[] PegarItem;
public MatarGente[] MatarGente;
}
public Missoes[] MissoesJogasso;
}
I only what to show the class PegarItem if PegarItem was chosen in the enum.
There's only PegarItem and MatarGente right now, but there will be more classes.
I did some research and find out Im supposed to use the OnInspectorGUI if I want to be that specific (if there's other way round, pls tell me)
I got 0 experience on CustomEditor so what I got so far is
[CustomEditor(typeof(ClasseTest))]
public class TestCustomInspector : Editor
{
public int numMissoes;
public override void OnInspectorGUI()
{
ClasseTest script = (ClasseTest)target;
numMissoes = EditorGUILayout.IntField("Numero de Missoes", numMissoes);
EditorGUILayout.LabelField("Editante");
var serializedObject = new SerializedObject(target);
var property = serializedObject.FindProperty("MissoesJogasso");
serializedObject.Update();
EditorGUILayout.PropertyField(property, true);
serializedObject.ApplyModifiedProperties();
for (int i = 0; i < numMissoes; i++)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("TIPO DE MISSAO", script.TiposMissoes);
if (script.TiposMissoes == ClasseTest.TiposDeMissoes.TipoMatarGente)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Matar Gentes", script.TiposMissoes);
}
if (script.TiposMissoes == ClasseTest.TiposDeMissoes.TipoPegarItem)
{
script.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Pegar Item", script.TiposMissoes);
}
}
}
}
And that means, in the editor:
If I change the value of one enum, all the others copy it (image)
And That's exactly what I do not want.
And y'all, keep in mind when the editor chooses MatarGente or PegarItem I'd like to show all those possible variables that these classes hold. That includes arrays with non-specific lengths.
And also, if there's 2 'MatarGente' I'd like to be able to fill those arrays with different objects and retrieve that information later to use it somewhere else.
Tried to understand what you were doing. Your mistake was not modifying the array elements and few other issues.
This is your new ClasseTest:
using System;
using UnityEngine;
using System.Collections.Generic;
public class ClasseTest : MonoBehaviour
{
public enum TiposDeMissoes
{
TipoMatarGente,
TipoPegarItem,
}
[Serializable]
public class MatarGente
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] InimigosPraMatar;
}
[Serializable]
public class PegarItem
{
public int IDmissao;
public GameObject[] ObjetosAtivarPraMissao;
public GameObject[] Checkpoints;
public GameObject[] ItemsEntregar;
}
[Serializable]
public class Missoes
{
public TiposDeMissoes TiposMissoes;
public PegarItem[] PegarItem;
public MatarGente[] MatarGente;
}
public List<Missoes> MissoesJogasso;
public int NumMissoes;
}
This is your new TestCustomInspector class:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
[CustomEditor(typeof(ClasseTest))]
public class TestCustomInspector : Editor
{
public override void OnInspectorGUI()
{
ClasseTest script = (ClasseTest)target;
script.NumMissoes = EditorGUILayout.IntField("Numero de Missoes", script.NumMissoes);
// Ensure it cannot go into negative numbers.
if (script.NumMissoes < 0) script.NumMissoes = 0;
// Create the list if it does not exist.
if(script.MissoesJogasso == null) script.MissoesJogasso = new List<ClasseTest.Missoes>();
// numMissoes being greater than the current count means we need to extend the list.
if (script.NumMissoes > script.MissoesJogasso.Count)
{
for (int i = 0; i < script.NumMissoes; i++)
{
script.MissoesJogasso.Add(new ClasseTest.Missoes());
}
}
// numMissoes being less than the current count means we need to decrease the list.
else if(script.NumMissoes < script.MissoesJogasso.Count)
{
int difference = script.MissoesJogasso.Count - script.NumMissoes;
// Remove the last element difference number of times.
for (int i = 0; i < difference; i++)
{
script.MissoesJogasso.RemoveAt(script.MissoesJogasso.Count - 1);
}
}
var serializedTarget = new SerializedObject(target);
for (int i = 0; i < script.MissoesJogasso.Count; i++)
{
var missoes = script.MissoesJogasso[i];
switch(missoes.TiposMissoes)
{
case ClasseTest.TiposDeMissoes.TipoMatarGente:
missoes.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Matar Gentes", missoes.TiposMissoes);
DrawProperty(serializedTarget, string.Format("MissoesJogasso.Array.data[{0}].MatarGente", i));
break;
case ClasseTest.TiposDeMissoes.TipoPegarItem:
missoes.TiposMissoes = (ClasseTest.TiposDeMissoes)EditorGUILayout.EnumPopup("Pegar Item", missoes.TiposMissoes);
DrawProperty(serializedTarget, string.Format("MissoesJogasso.Array.data[{0}].PegarItem", i));
break;
}
}
}
private void DrawProperty(SerializedObject serializedObject, string propertyName)
{
var property = serializedObject.FindProperty(propertyName);
serializedObject.Update();
EditorGUILayout.PropertyField(property, true);
serializedObject.ApplyModifiedProperties();
}
}
In Unity:
Hope this works.

Sync Random Numbers In UnityNetwork

I want to assign random numbers to my objects that spawn when the server starts ,But every instance of the game makes its own random numbers and they don't sync together ,How can i fix that?
public class Health : NetworkBehaviour
{
public Text PlanetHealth;
public const int maxHealth = 100;
[SyncVar(hook = "OnChangeHealth")]
public int currentHealth = maxHealth;
void Start()
{
if(this.gameObject.name == "GrayPlanet(Clone)")
{
currentHealth = Random.Range(0, 50);
PlanetHealth.text = currentHealth.ToString();
}
else
PlanetHealth.text = maxHealth.ToString();
}
public void TakeDamage(int amount)
{
if (!isServer)
{
return;
}
currentHealth -= amount;
if (currentHealth <= 0)
{
currentHealth = 55;
}
}
void OnChangeHealth(int currentHealth)
{
PlanetHealth.text = currentHealth.ToString();
}
}
Just add this at the beginning of your Start() method:
if (!isServer)
{
return;
}
You should do the random generation only in the server side. After that, SyncVar will do the job of keeping the instances up to date.

Am I Doing Something Wrong here Unity 3D

I've been stuck on this script for about some weeks. I have this script where as to the items that are on a list shuffle every time a new game starts. I don't know why but I can't seem to shuffle an item list at all. Please help, and tested it for yourself if you can. Also please explain to me what might be the problem or what I can do to fix it, I'm still pretty new to this.
`using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class RayCasting : MonoBehaviour
{
public float pickupDistance;
public List<Item> items;
public List<Item> finalItems;
#region Unity
void Start ()
{
Screen.lockCursor = true;
// Do a while loop until finalItems contains 5 Items
while (finalItems.Count < 5) {
Item newItem = items[Random.Range(0, items.Count)];
if (!finalItems.Contains(newItem)) {
finalItems.Add(newItem);
}
items.Clear();
}
}
void Update ()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out hit, pickupDistance))
{
foreach(Item item in finalItems)
{
if(Input.GetMouseButtonDown(0)) {
if (item.gameObject.Equals(hit.collider.gameObject))
{
numItemsCollected++;
item.Collect();
break;
}
}
}
}
}
void OnGUI()
{
GUILayout.BeginArea(new Rect(130,400,100,130));
{
GUILayout.BeginVertical();
{
if (numItemsCollected < items.Count)
{
foreach (Item item in finalItems)
GUILayout.Label(string.Format("[{0}] {1}", items.Collected ? "" + items.password: " ", items.name ));
}
else
{
GUILayout.Label("You Win!");
}
}
GUILayout.EndVertical();
}
GUILayout.EndArea();
}
#endregion
#region Private
private int numItemsCollected;
#endregion
}
[System.Serializable]
public class Item
{
public string name;
public GameObject gameObject;
public int password;
public bool Collected { get; private set; }
public void Collect()
{
Collected = true;
gameObject.SetActive(false);
}
public void passwordNumber()
{
password = 0;
Collected = true;
gameObject.SetActive(false);
}
}
`
This example is too complicated, and has a lot of unrelated code to the question you seem to be asking. As a basic strategy, you should try to isolate the code you think has a problem, fix that problem, then apply it to the larger case.
For example, your basic logic has at least one error: Random.Range is inclusive, so you could possibly have an out-of-bounds index unless you use "index.Count-1".
Here's an example that creates a list of 5 random items from a larger "original" list. You should be able to apply this to your code:
using UnityEngine;
using System.Collections.Generic;
public class RandomList : MonoBehaviour {
public List <int> originals = new List<int>();
public List <int> randomized = new List<int>();
public int desiredNumberOfRandomInts = 5;
// Use this for initialization
void Awake () {
for(int i = 1; i <= 20; ++i) {
originals.Add(i);
}
while(randomized.Count < desiredNumberOfRandomInts) {
int randomSelection = originals[Random.Range(0, originals.Count-1)];
if (!randomized.Contains(randomSelection)) {
randomized.Add(randomSelection);
}
}
}
}
I think removing the items.Clear() might work, with the reason I explained in my comment.
while (finalItems.Count < 5) {
Item newItem = items[Random.Range(0, items.Count)];
if (!finalItems.Contains(newItem)) {
finalItems.Add(newItem);
}
}
But still, I'm assuming you already have list of items somewhere already (and the list must be >5 since you will loop minimum 5 times). If you don't items.Count will still stay 0, and making your random become Random.Range(0,0).

Categories