Random Shuffle Listing in Unity 3D - c#

I'm in a little bit of a pickle. I have this script of an array that shows a listing of items. Now the thing is I only want this list to have five items shown out of ten and also shuffled, so you cant have the same list every time you start a new game. I was thinking if there should be a Random.Range implemented but I dont know where. Please Help and Thanks. Heres the script: `
public class RayCasting : MonoBehaviour
{
public float pickupDistance;
public List<Item> items;
#region Unity
void Start ()
{
Screen.lockCursor = true;
}
void Update ()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out hit, pickupDistance))
{
foreach(Item item in items)
{
if(Input.GetMouseButtonDown(0)) {
if (item.gameObject.Equals(hit.collider.gameObject))
{
numItemsCollected++;
item.Collect();
break;
}
}
}
}
}
void OnGUI()
{
GUILayout.BeginArea(new Rect(130,400,100,100));
{
GUILayout.BeginVertical();
{
if (numItemsCollected < items.Count)
{
foreach (Item item in items)
GUILayout.Label(string.Format("[{0}] {1}", item.Collected ? "X" : " ", item.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 bool Collected { get; private set; }
public void Collect()
{
Collected = true;
gameObject.SetActive(false);
}
}
`

To get a random 5 items from your 10 item list you can use:
List<Items> AllItems = new List<Items>();
List<Items> RandomItems = new List<Items>();
Random random = new Random();
for(int i = 0; i < 5; i++)
{
RandomItems.Add(AllItems[random.Next(0, AllItems.Count + 1)]);
}
The AllItems list contains your 10 items.
After the loop you will have 5 random items inside the RandomItems list.

Just figured it out, this is my solution for shuffle any kind of List
public class Ext : MonoBehaviour
{
public static List<T> Shuffle<T>(List<T> _list)
{
for (int i = 0; i < _list.Count; i++)
{
T temp = _list[i];
int randomIndex = Random.Range(i, _list.Count);
_list[i] = _list[randomIndex];
_list[randomIndex] = temp;
}
return _list;
}
}
With your example it should work like this:
AllItems = Ext.Shuffle<Items>(AllItems);
Debug.Log(AllItems[0]); // Will be always random Item
If you need 5 random Items, you can just call
Debug.Log(AllItems[0]); // Random Item
Debug.Log(AllItems[1]); // Random Item
Debug.Log(AllItems[2]); // Random Item
Debug.Log(AllItems[3]); // Random Item
Debug.Log(AllItems[4]); // Random Item

Related

How to make loot table not repeat items in Unity?

I am looking to randomly generate 3 items using a loot table. The loot table currently works as expected, it will randomly generate an item based on the item's rarity. However, I don't want there to be duplicates of the same item. How can I set this up? This script is currently attached to 3 separate GameObject item pedestals.
using System.Collections.Generic;
using UnityEngine;
public class ShopItemSpawner : MonoBehaviour
{
[System.Serializable]
public class DropItem
{
public string name;
public GameObject item;
public int dropRarity;
}
public List<DropItem> ShopItemPool = new List<DropItem>();
private void Start()
{
int itemWeight = 0;
for (int i = 0; i < ShopItemPool.Count; i++)
{
itemWeight += ShopItemPool[i].dropRarity;
}
int randomValue = Random.Range(0, itemWeight);
for (int j = 0; j < ShopItemPool.Count; j++)
{
if (randomValue <= ShopItemPool[j].dropRarity)
{
Instantiate(ShopItemPool[j].item, transform.position, Quaternion.identity);
return;
}
randomValue -= ShopItemPool[j].dropRarity;
}
}
}
You could clone the ShopItemPool list and with each item rolled you remove that item from the list. You then need to go all over again by recalculating the total weight.
I find it useful to have a general purpose class for randomizing items with weights, it allows you do this this:
var randomizer = new WeightRandomizer<DropItem>();
foreach (var shopItem in ShopItemPool) {
randomizer.Add(shopItem, shopItem.dropRarity);
}
randomizer.Roll(); // Get a random element based on weight.
randomizer.Take(); // Get a random element based on weight and remove it from the randomizer.
General Purpose Weight Randomizer:
public class WeightRandomizer<T> {
[Serializable]
public class WeightedElement<T> {
public T value;
public int weight;
public WeightedElement (T value, int weight) {
this.value = value;
this.weight = weight;
}
}
private readonly List<WeightedElement<T>> elements = new();
public void Add (T value, int weight) => elements.Add(new WeightedElement<T>(value, weight));
public void AddRange (IEnumerable<WeightedElement<T>> weightedElements) => elements.AddRange(weightedElements);
public int TotalWeight() => elements.Sum(x => x.weight);
public T Roll() => Pick(false);
public T Take() => Pick(true);
private T Pick (bool remove) {
if (elements.Count == 0) {
Debug.LogWarning($"{nameof(WeightRandomizer<T>)} is missing elements.");
return default(T);
}
var roll = Random.Range(0, TotalWeight());
var selectedIndex = elements.Count - 1;
var selected = elements[selectedIndex].value;
for (var i = 0; i < elements.Count; i++) {
var element = elements[i];
// Found an element with a low enough value.
if (roll < element.weight) {
selected = element.value;
selectedIndex = i;
break;
}
// Keep searching for an element with a lower value.
roll -= element.weight;
}
// Sometimes we want to take and remove the element from the pool.
if (remove) elements.RemoveAt(selectedIndex);
return selected;
}
}

C# Unity Inventory - List.FindAll() not working as expected

I am super new to C#, so apologies if this is a simple question or has already been answered. I've been looking at other SO questions, trying the exact methods they use, and am still having no luck.
In Unity, I have an Inventory Object class that I can use to create Inventories in my game:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory System/Inventory")]
public class InventoryObject : ScriptableObject
{
public List<InventorySlot> Container = new List<InventorySlot>();
public void AddItem(ItemObject newItem, int itemAmount)
{
bool inventoryHasItem = false;
for (int i = 0; i < Container.Count; i++)
{
if (CurrentSlotHasItem(Container[i], newItem)) {
if (Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
if (!inventoryHasItem)
{
Container.Add(new InventorySlot(newItem, itemAmount));
}
}
private bool CurrentSlotHasItem(InventorySlot currentSlot, ItemObject item)
{
return currentSlot.item == item;
}
}
[System.Serializable]
public class InventorySlot
{
public ItemObject item;
public int amount;
public InventorySlot(ItemObject _item, int _amount)
{
item = _item;
amount = _amount;
}
public void AddAmount(int value)
{
amount += value;
}
}
This works great, except for this line:
Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize
For some reason, no matter what I use for the findAll() predicate, I always get the same amount in my inspector - 1. Which means that .Count never goes above 1, and I can go way above my ItemObject.maxStackSize.
This is an example ItemObject class I have:
using UnityEngine;
[CreateAssetMenu(fileName = "New Food Object", menuName = "Inventory System/Items/Food")]
public class FoodObject : ItemObject
{
public int healthAmount;
private void Awake() {
type = ItemType.Food;
maxStackSize = 25;
}
}
This is also my Player script that adds the items to the inventory. It just adds them based off of OnTriggerEnter.
public class Player : MonoBehaviour
{
public InventoryObject inventory;
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<Item>();
if (item)
{
inventory.AddItem(item.item, 1);
Destroy(other.gameObject);
}
}
}
Here's some screenshots of my Unity console/inspector, with these two lines added to my InventoryObject class. You can see that .Count never going above 1.
if (CurrentSlotHasItem(Container[i], newItem)) {
Debug.Log(Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count);
Debug.Log(Container[i].amount);
// rest of class
I don't know what I was thinking here. All I needed to do to get maxStackSize to work was changing the logic inside of AddItem:
for (int i = 0; i < Container.Count; i++)
{
if (Container[i].item.id == newItem.id) {
if (Container[i].amount < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
It just compares Container[i].amount to newItem.maxStackSize. If it's under, it will stack the item. Otherwise, it creates a new item in the inventory.

iterate through dictionary with array c#

I have a dictionary with gameobjects as the keys, which is n amount and gameobjects as the values, which is amount of possible 4.
Dictionary<GameObject, ArrayList> polesAttachedToFloor = new Dictionary<GameObject, ArrayList>();
public void AddFloor(GameObject floor)
{
if(floor != null)
{
polesAttachedToFloor.Add(floor, new ArrayList { null,null,null,null });
}
}
public void AddPole(GameObject floor, GameObject pole)
{
for (int i = 0; i <= 4; i++)
{
}
}
How do I iterate through the "values?"... and is there a more 'appropriate' way of doing what Im aiming for?
ArrayList poles;
if (polesAttachedToFloor.ContainsKey(floor))
{
poles = dictionary[floor];
}
if(poles!=null)
{
for (int i = 0; i <= 4; i++)
{
poles.Add(pole);
}
}
Try that inside you AddPole function.
There is how you should encapsulate that...Dont have unity running so may be some typos.
public class FloorManager:MonoBehaviour
{
public gameObject floorPrefab;
private List<Floor> floors;
void Start()
{
floors = new List<Floor>();
}
public void AddFloor()
{
//instantiate prefab // gameObject floorGo = Instantiate....
Floor floorScript = floorGo.getComponent<Floor>();
floors.Add(floorScript);
floorScript.AddPoles();
}
public void RemoveFloor(Floor floor)
{
floors[floors.IndexOf(floor)].gameObject.Destory();
floors.Remove(floor);
}
}
public class Floor : MonoBehaviour
{
public gameObject polePrefab;
public Pole [] poles = new Pole[4];
public void AddPoles()
{
for (int i = 0; i < 4; i++)
{
//instantiate prefab // gameObject poleGo = Instantiate....
Pole poleScript = poleGo.getComponent<Pole>();
poles[i]=poleScript;
}
}
}
public class Pole : MonoBehaviour
{
//some logic here if needed...Destroy...Damage...
}
Play with Enumerator :D Have fun
public void AddPole(GameObject floor, GameObject pole)
{
var enumerator = polesAttachedToFloor.GetEnumerator ();
for (int i = 0; i <= 4; i++)
{
if (enumerator.MoveNext ()) {
var item = enumerator.Current;
}
}
}

Scriptable object doesn't work on build

I'm hoping this might be something simple I'm missing.
I have a ScriptableObject script that looks like this:
using UnityEngine;
using System.Collections;
[CreateAssetMenu]
public class Item: ScriptableObject
{
public string iname;
public int iindex;
public Sprite sprite;
public GameObject itemObject;
public int value;
public string description;
public itemType itemtype;
public enum itemType
{
Consumable,
Equippable,
}
}
This works great in the editor, but if I publish to Android or Windows any script that references the ScriptableObject, it does not work. What am I missing?
For example the following block of code does not seem to execute at all:
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
Those lists in the code are of the type Item defined in the above script. I'm not sure how to debug this as I get no errors or warnings in the editor.
Here is the script which populates the inventory. There's a bit of junk in there but it definitley works fine pressing play in the editor. Just not on build.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
public int invSize;
public Item slot1;
public Item tslot1;
public Item tslot2;
public Item tslot3;
public GameObject t1;
public GameObject t2;
public GameObject t3;
public Sprite itemsprite;
public List<Item> itemdb = new List<Item>();
public List<Item> items = new List<Item>();
public List<Item> tlist = new List<Item>();
public Text stext;
public Text description;
public Item selectItem;
public GameObject selectSlot;
public Object test2;
public List<GameObject> slotlist = new List<GameObject>();
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void addItem(Item itemToAdd)
{
//items.Add(itemdb[0]);
for (int i = 0; i < 5; i++)
{
if (items[i] == null)
{
items[i] = itemToAdd;
itemsprite = itemToAdd.sprite;
return;
}
}
}
public void GenTreasure()
{
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
}
public void Uptext(int indexx)
{
stext.text = tlist[indexx].iname;
selectItem = tlist[indexx];
selectSlot = slotlist[indexx];
description.text = selectItem.description;
}
public void Take(int index)
{
//items.Add(selectItem);
for (int i = 0; i < invSize; i++)
{
if (items[i] == null)
{
items[i] = selectItem;
// itemsprite = itemToAdd.sprite;
selectItem = null;
// tlist[i] = null;
// slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = null;
selectSlot.transform.GetChild(0).GetComponent<Image>().enabled = false;
return;
}
}
}
}
If the script is not in the scene, how does it get loaded? Is it using an asset bundle? If that is the case, then it's possible that the class is being stripped from the build. You can include the class in your link.xml to make sure it's included. Another option is to simply reference the script in an included scene anywhere.

Inheritance - storing objects list<class> with loops C#

I'm stuck one an exercise which is very important to understand for my soon come final exam, in a basic C# course.
I have an issue that I can't describe in words. So I'll show you with code and perhaps then you might help me.
I've been stuck on this, not being able to solve it, for a long time. Cuz of that, I don't want any copy paste code. I want to understand it. So please enlighten me where I've failed.
Explanation what I want to do.
I want to create a dart game 501. So first I add players, they throw their darts, you get the score after each turn announced, then when one player reach 501 points, the game announce the winner and all of his throws.
My idea of approaching the problem is to have an add player loop, which terminates (I got this fixed already).
Once you done creating the players(list elements), then you'll execute the methods with a foreach loop running all the players in the player list, executing the objects one at a time and finally Here is my real problem: storing all their scores in another list.
Here we go with the code
The list.
private List<Player> players = new List<Player>(); //This list is fine
Here is the loop.
foreach (Player dartThrows in players) //My loop
{
dartThrows.DoThrow();
dartThrows.GetScore();
}
SubClass1(named Player)
public List<Darts> dartList = new List<Darts>(); //<--HERE IS THE PROBLEM
Just some random constructors and methods.
The throw method. It's not an issue but I typed it down to give you an idea
public void DoThrow()
{
var tries = 3;
for (int i = 0; i < tries; i++)
{
//No problems here just int read-user input data
}
AddDarts(int a, int b, intc)
}
Here lies all my problems, it would make my life so much easier if this could get solved.
public void AddDarts(Darts toDartList)
{
dartList.Add(toDartList);
}
SubClass2 (Darts)
Here are my constructors
private int dartOne;
private int dartOne;
private int dartOne;
Here is my method
public Darts(int DartOne, int DartTwo, int DartThree)
{
dartOne = DartOne;
dartTwo = DartTwo;
dartThree = DartThree;
}
Best regards Marcus Johansson
Here is my full program
class Program
{
static void Main(string[] args)
{
Game game = new Game();
game.PlayGame();
}
}
class Game
{
private List<Player> players = new List<Player>();
private List<Player> computers = new List<Player>();
public void AddPlayer(string newPlayers)
{
players.Add(new Player(newPlayers));
}
public void AddComputer(string newComputer)
{
computers.Add(new Player(newComputer));
}
static string UpperCaseFirst(string s)
{
if (string.IsNullOrEmpty(s))
{
return string.Empty;
}
return char.ToUpper(s[0]) + s.Substring(1);
}
public void PlayGame()
{
bool noWinner = false;
bool stopLoop = false;
Console.WriteLine("<<<WELCOME TO DART 501>>>");
Console.WriteLine("\nPress [S] to start game!");
Console.Beep();
Console.ReadLine();
Console.Clear();
do
{
Console.WriteLine("Enter name of players and type [D]ator for adding NPC\nType [S]top to start the game");
string addPlayer = Console.ReadLine();
string FirstUpperLetter = UpperCaseFirst(addPlayer);
if (FirstUpperLetter == "Stop" || FirstUpperLetter == "S")
{
stopLoop = true;
}
if (FirstUpperLetter == "D" || FirstUpperLetter == "Dator")
{
string computer = FirstUpperLetter;
AddComputer(computer);
}
else
{
AddPlayer(FirstUpperLetter);
}
} while (stopLoop == false) ;
players.RemoveAt(players.Count - 1);
do
{
Console.Clear();
foreach (Player arrowThrows in players)
{
noWinner = true;
Console.WriteLine("\n~~~Starting Round~~~~");
arrowThrows.DoThrow();
Console.WriteLine("This round you got {0}", arrowThrows.CalculatePoints());
if (arrowThrows.Score > 501)
{
Console.Clear();
Console.WriteLine("<<<WE HAVE A WINNER>>>");
System.Threading.Thread.Sleep(500);
Console.WriteLine("...The winner is: ");
System.Threading.Thread.Sleep(1000);
Console.WriteLine("{0} He made these epic throws: ", arrowThrows.Name);
foreach(Arrows finalResult in arrowThrows.arrowList)
{
Console.WriteLine(finalResult);
Console.ReadLine();
}
noWinner = false;
}
}
foreach (Player computerThrows in computers)
{
computerThrows.RandomThrow();
System.Threading.Thread.Sleep(500);
}
}while(noWinner == true);
}
}
class Player
{
public List<Arrows> arrowList = new List<Arrows>();
public List<int> ScoreBoard = new List<int>();
public Player() { }
public int Score { get; set; }
public Player(int score)
{
Score = score;
}
public string Name { get; set; }
public Player(string name)
{
Name = name;
}
public int RoundScore { get; set; }
public void RandomThrow()
{
Random rndComp = new Random();
Console.WriteLine("...Loading Npc_throw.exe");
System.Threading.Thread.Sleep(300);
int random1 = rndComp.Next(0, 60);
System.Threading.Thread.Sleep(300);
int random2 = rndComp.Next(0, 60);
System.Threading.Thread.Sleep(300);
int random3 = rndComp.Next(0, 60);
Console.WriteLine("Random computer got this random score {0}", random1 + random2 + random3);
arrowList.Add(new Arrows(random1, random2, random3));
}
public void DoThrow()
{
Console.WriteLine("###{0} make your throws###", Name);
var tries = 3;
for (int i = 0; i < tries; i++)
{
Console.WriteLine("\nEnter score for {0} arrow", i + 1);
string arrowScore = Console.ReadLine();
int addScore = int.Parse(arrowScore);
while(-1 > addScore || 61 < addScore)
{
Console.WriteLine("\nInvalid score! Enter a score between 0-60/n<<<You may type [R]andom or [R] for a random score>>>");
arrowScore = Console.ReadLine().ToUpper();
if (arrowScore == "R" || arrowScore == "RANDOM")
{
Random rnd = new Random();
addScore = rnd.Next(0, 60);
goto start;
}
else
{
addScore = int.Parse(arrowScore);
}
}
start:
ScoreBoard.Add(addScore);
}
ScoreBoard.ToArray();
arrowList.Add(new Arrows(ScoreBoard[0],ScoreBoard[1], ScoreBoard[2]));
}
public int CalculatePoints()
{
Score = ScoreBoard.Sum();
return Score;
}
public void AddArrows(Arrows toArrowList)
{
toArrowList.ToString();
arrowList.Add(new Arrows(ScoreBoard[0], ScoreBoard[1], ScoreBoard[2]));
}
}
class Arrows
{
private int arrowOne;
private int arrowTwo;
private int arrowThree;
public int score { get; set; }
public Arrows(int ArrowOne, int ArrowTwo, int ArrowThree)
{
arrowOne = ArrowOne;
arrowTwo = ArrowTwo;
arrowThree = ArrowThree;
}
public int GetScore()
{
return arrowOne + arrowTwo + arrowThree;
}
public override string ToString()
{
return string.Format("{0}-1:st arrow: {1}-2:nd arrow: {2}- 3:rd arrow: {3}", GetScore(), arrowOne, arrowTwo, arrowThree);
}
}
}
The problem with your approach, is your utilizing a List. This collection has no unique information, simply a grouping of data. Which would have me recommend either a Tuple, Dictionary, KeyValuePair, or a clean Data Model.
Without the requirements are approaches may not be viable. Your issue stems from persistence related issues. Your multiple inheritance title is also incorrect, C# only allows a single class to be inherited.
What I would potentially do would be:
public class Player
{
public string Name { get; set; }
public string Throw { get; set; }
public string Points { get; set; }
}
What this does, is incredibly beneficial. The above class represents a specific Model. When your method is called for a throw you apply said results. Then when you calculate the score you iterate through the List<Player> for a specific person or all and display the output.
I'm not going to write your application, but think of it like this:
Player Registers
Player Turn
Player Throws
Play Scored
Store Player Result
Repeat Steps two through five, three times.
Display Output: Player, Throw, Score, and Total Score
By utilizing that model, you hold the relative information for your outcome. This conceptual notion is important for Object Oriented Programming.
You can use a Dictionary to save scores for each player
Dictionary<Player, List<int>> playerScores = new Dictionary<Player, List<int>>();
playerScores.Add(player1, new List<int>()); //init dictionary with a new player
playerScores[player1].Add(number); //add a value to the list associated with the player
So each player has a list of scores.

Categories

Resources