Shuffling Items in Unity 3D - c#

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 can't have the same list every time you start a new game
I was thinking if there should be a Random.Range implemented but I don't know where.
Please Help, and explain what should be done. I'm still a bit new to this and Thanks.
Here's the script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
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 ? "" + item.password: " ", 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 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);
}
}

I assume you'll want to leave items intact without removing any Items, so I'd suggest creating a second List called finalItems, which will contain your 5 random Items.
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);
}
}
}
And then in your foreach statements, loop through finalItems instead of items.
This will give you 5 random Items every game!

Related

Hello, how could i improve this inventory system to be able to swap items, stacking items on dragging?

I'm really just a beginner and trying to learn game development, but i can't seem to figure out the logic behind this.. if the inventory has already an item inside and if the current object can't be stacked on top of it, then swap out the 2 object
the script i'm trying to implement it:
public void OnDrop(PointerEventData eventData)
{
//on drop if the inventory slot doesn't have child object then we know it's a free slot, so we can drag there the grabbed item.
if(transform.childCount == 0)
{
GameObject dropped = eventData.pointerDrag;
InventoryItem inventoryItem = dropped.GetComponent<InventoryItem>();
inventoryItem.parentAfterDrag = transform;
}
else
{
//on drop if the inventory has already an item inside and if the current object can't be stacked on top of it, then swap out the 2 object
//but how do i know what is the object that is inside this slot?
}
}
}
here is the InventoryManager script:
public class InventoryManager : MonoBehaviour
{
public int maxStackedItems = 4;
int selectedSlot = -1;
public InventorySlot[] inventorySlots;
public GameObject inventoryItemPrefab;
public GameObject mainInventory;
public bool isMainInventoryOpened;
public bool AddItem(Item item)
{
//checks if there is any slot with the same kind of item as this, which isnt in max stack
for (int i = 0; i < inventorySlots.Length; i++)
{
InventorySlot slot = inventorySlots[i];
InventoryItem itemInSlot = slot.GetComponentInChildren<InventoryItem>();
if (itemInSlot != null && itemInSlot.item == item && itemInSlot.count < maxStackedItems && itemInSlot.item.stackable == true)
{
itemInSlot.count++;
itemInSlot.RefreshCount();
return true;
}
}
//looks for an empty slot
for (int i = 0; i < inventorySlots.Length; i++)
{
InventorySlot slot = inventorySlots[i];
InventoryItem itemInSlot = slot.GetComponentInChildren<InventoryItem>();
if(itemInSlot == null)
{
SpawnNewItem(item, slot);
return true;
}
}
return false;
}
void SpawnNewItem(Item item, InventorySlot slot)
{
GameObject newItemGo = Instantiate(inventoryItemPrefab, slot.transform);
InventoryItem inventoryItem = newItemGo.GetComponent<InventoryItem>();
inventoryItem.InitializeItem(item);
}
public Item GetSelectedItem(bool use)
{
InventorySlot slot = inventorySlots[selectedSlot];
InventoryItem itemInSlot = slot.GetComponentInChildren<InventoryItem>();
if (itemInSlot != null)
{
Item item = itemInSlot.item;
if(use == true)
{
itemInSlot.count--;
if(itemInSlot.count <= 0)
{
Destroy(itemInSlot.gameObject);
}
else
{
itemInSlot.RefreshCount();
}
}
return item;
}
return null;
}
}
and the InventoryItem script:
public class InventoryItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[HideInInspector] public Transform parentAfterDrag;
[HideInInspector] public Item item;
[HideInInspector] public int count = 1;
public Image image;
public TMP_Text countText;
public void InitializeItem (Item newItem)
{
item = newItem;
image.sprite = newItem.image;
RefreshCount();
}
public void RefreshCount()
{
countText.text = count.ToString();
bool textActive = count > 1;
countText.gameObject.SetActive(textActive);
}
public void OnBeginDrag(PointerEventData eventData)
{
parentAfterDrag = transform.parent;
transform.SetParent(transform.root);
transform.SetAsLastSibling();
image.raycastTarget = false;
}
public void OnDrag(PointerEventData eventData)
{
transform.position = Input.mousePosition;
}
public void OnEndDrag(PointerEventData eventData)
{
transform.SetParent(parentAfterDrag);
image.raycastTarget = true;
}
}
i've tried to figure out what is the item on that occupied slot but i don't seem to understand this logic yet.

UI element keeps upscaling itself upon game start

Pretty new to developing so please excuse any obvious errors.
I'm currently trying to build an inventory system for my game but the item slots keep upscaling themselves upon game start.
The prefab starts with a scaling of: x=1, y=1, z=1;
On game start this inflates to: x=53.1, y=53.1, z=53.1;
Here is the scale of the prefab:HERE
And here is the scale of the prefab upon game start: HERE
Here is the script to update the items:
using UnityEngine;
using UnityEngine.UI;
public class UIItem : MonoBehaviour
{
public Item item;
private Image spriteImage;
private void Awake()
{
spriteImage = GetComponent<Image>();
UpdateItem(null);
}
public void UpdateItem(Item item)
{
this.item = item;
if (this.item != null)
{
spriteImage.color = Color.white;
spriteImage.sprite = this.item.icon;
}
else
{
spriteImage.color = Color.clear;
}
}
}
And here is the script used to update the slots and to add the items to the inventory:
using System.Collections.Generic;
using UnityEngine;
public class UIInventory : MonoBehaviour
{
public List<UIItem> uIItems = new List<UIItem>();
public GameObject slotPrefab;
public Transform slotPanel;
public int numberOfSlots = 16;
private void Awake()
{
for (int i = 0; i < numberOfSlots; i++)
{
GameObject instance = Instantiate(slotPrefab);
instance.transform.SetParent(slotPanel);
uIItems.Add(instance.GetComponentInChildren<UIItem>());
}
}
public void UpdateSlot(int slot, Item item)
{
uIItems[slot].UpdateItem(item);
}
public void AddNewItem(Item item)
{
UpdateSlot(uIItems.FindIndex(i => i.item == null), item);
}
public void RemoveItem(Item item)
{
UpdateSlot(uIItems.FindIndex(i => i.item == item), null);
}
}
Here is the inventory script itself:
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public List<Item> characterItems = new List<Item>();
public ItemDatabase itemDatabase;
public UIInventory inventoryUI;
private void Start()
{
GiveItem(1);
GiveItem("Clue 5");
RemoveItem(1);
}
public void GiveItem(int id)
{
Item itemToAdd = itemDatabase.GetItem(id);
characterItems.Add(itemToAdd);
inventoryUI.AddNewItem(itemToAdd);
Debug.Log("Added item:" + itemToAdd.name);
}
public void GiveItem(string itemName)
{
Item itemToAdd = itemDatabase.GetItem(itemName);
characterItems.Add(itemToAdd);
inventoryUI.AddNewItem(itemToAdd);
Debug.Log("Added item:" + itemToAdd.name);
}
public Item CheckForItem(int id)
{
return characterItems.Find(item => item.id == id);
}
public void RemoveItem(int id)
{
Item item = CheckForItem(id);
if (item != null)
{
characterItems.Remove(item);
inventoryUI.RemoveItem(item);
Debug.Log("Item removed:" + item.name);
}
}
}
I've tinkered on this for the past four hours and have nothing that seems to work. I've tried the Aspect Ratio Fitter and it only made it more. I can try to explain further if needed.
If anyone could help, it would be greatly appreciated.

Implement stacking to my inventory system in Unity

I'm currently working on a game, and now I've just finished the inventory system, which is highly inspired by the one Brackeys made a while back. Right now the player can pick up items, which will go into the inventory's list and be displayed on some UI. After having finished this, I tried to make items stack together, however I was never able to find a working solution. I don't want anything fancy, I just want every item to be able to stack infinitely. If anyone could help me out I'd greatly appreciate it!
Sorry for putting so much code in here, I'm not sure what I should and shouldn't add.
My "Items" script:
using UnityEngine;
[CreateAssetMenu(fileName = "New item", menuName = "Inventory/Items")]
public class Item : ScriptableObject
{
new public string name = "New item";
public Sprite icon = null;
public bool itemDefaut = false;
public virtual void Use()
{
Debug.Log("Using " + name);
}
}
My "Inventory" script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
#region Singleton
public static Inventory instance;
void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one inventory instance found!");
return;
}
instance = this;
}
#endregion
public delegate void OnItemChanged();
public OnItemChanged onItemChangedCallback;
public List<Item> items = new List<Item>();
public void Add(Item item)
{
items.Add(item);
if (onItemChangedCallback != null)
{
onItemChangedCallback.Invoke();
}
}
public void Remove(Item item)
{
items.Remove(item);
if (onItemChangedCallback != null)
{
onItemChangedCallback.Invoke();
}
}
}
My "InventorySlot" script:
using UnityEngine;
using UnityEngine.UI;
public class InventorySlot : MonoBehaviour
{
Item item;
public Image icon;
public void AddItem(Item newItem)
{
item = newItem;
icon.sprite = item.icon;
icon.enabled = true;
}
public void RemoveItem()
{
item = null;
icon.sprite = null;
icon.enabled = false;
}
public void UseItem()
{
if (item != null)
{
item.Use();
}
}
}
My "InventoryUI" script:
using UnityEngine;
public class InventoryUI : MonoBehaviour
{
public Transform itemsParent;
public GameObject inventoryUI;
Inventory inventory;
InventorySlot[] slots;
// Start is called before the first frame update
void Start()
{
inventory = Inventory.instance;
inventory.onItemChangedCallback += UpdateUI;
slots = itemsParent.GetComponentsInChildren<InventorySlot>();
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Inventory"))
{
inventoryUI.SetActive(!inventoryUI.activeSelf);
}
if (inventoryUI.activeSelf)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
void UpdateUI()
{
for (int i = 0; i < slots.Length; i++)
{
if (i < inventory.items.Count)
{
slots[i].AddItem(inventory.items[i]);
}
else
{
slots[i].RemoveItem();
}
}
}
}
My "ItemPickup" script:
using UnityEngine;
public class ItemPickup : Interactable
{
public Item item;
public override void Interact()
{
base.Interact();
PickUp();
}
void PickUp()
{
Debug.Log("Picking up " + item.name);
Inventory.instance.Add(item);
Destroy(gameObject);
}
}
And my "Interactable" script:
using UnityEngine;
public class Interactable : MonoBehaviour
{
public float radius = 3f;
[SerializeField] Player player;
bool interacted = false;
void Start()
{
interacted = false;
}
public virtual void Interact()
{
//This method is meant to be overwritten
Debug.Log("Interacting with " + transform.name);
}
void Update()
{
if (!interacted)
{
float distance = Vector3.Distance(player.transform.position, transform.position);
if (distance <= radius)
{
Interact();
interacted = true;
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
Finally, here's what my hierarchy looks like for my inventory (just replace "inventaire" by "inventory" and don't mind the item names):
If I understand you correctly it sounds like you basically want a counter for the slot so you can add multiple of the Item instance.
You could probably do something like
public class InventorySlot : MonoBehaviour
{
// Have a counter for the amount of items added
int _amount;
Item item;
public Image icon;
public Text amountText;
// optionally add an amount parameter
// if not passed 1 is used but allows to add multiple at once
public void AddItem(Item newItem, int amount = 1)
{
// If there is no item yet
// then simply add the first one
if(!item)
{
item = newItem;
icon.sprite = item.icon;
icon.enabled = true;
_amount = amount;
}
else
{
// Otherwise check first if it is the same item
if(item != newItem)
{
Debug.LogWarning($"Type mismatch between current item {item} and added item {newItem}!", this);
// TODO handle this?
return;
}
// simply increase the amount
_amount += amount;
}
// update text
amountText.text = _amount.ToString();
}
// Again maybe add optional parameter
// if nothing is passed 1 is used
// but allows to remove multiple at once
public void RemoveItem(int amount = 1)
{
// check if enough
if(!item)
{
Debug.LogWarning("This slot is empty! Can't remove", this);
// TODO handle this?
return;
}
if(_amount - amount < 0)
{
Debug.LogWarning($"Not enough items in this slot to remove {amount}!", this);
}
// simply reduce the amount
_amount -= amount;
// update the text
_amountText.text = amount.ToString();
// Only if you reached 0 -> removed the last item
// reset this slot
if(amount == 0)
{
item = null;
icon.sprite = null;
icon.enabled = false;
}
}
public void UseItem()
{
if (item)
{
item.Use();
// TODO also remove one?
}
}
}

OnMouseOver In Unity

quick question I got this script here to show the name of an Item you collect. These names are made public so I can change them within the item list. What I want to do Is to walk into an item and hover my mouse over the item so it can display the name of the item in the middle of the screen. I don't know if I should use a trigger, or a GUILayout or however. Please Help and thanks. Here is the Script: UPDATED
public class RayCasting : MonoBehaviour
{
public float pickupDistance;
public List<Item> items;
//public List<Keybutton> buttons;
#region Unity
void Start ()
{
Screen.lockCursor = false;
for (int i = 0; i < items.Count; i++) {
Item temp = items[i];
int randomIndex = Random.Range(i, items.Count);
items[i] = items[randomIndex];
items[randomIndex] = temp;
}
}
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()
{
GUI.backgroundColor = Color.blue;
GUI.Box(new Rect(120,390,170,250),"Text Message");
GUILayout.BeginArea(new Rect(132,432,100,170));
{
GUILayout.BeginVertical();
{
if (numItemsCollected < items.Count)
{
foreach (Item item in items)
GUILayout.Label(string.Format("[{0}] {1}", item.Collected ? "" + item.password: " ", item.name ));
}
else
{
foreach (Item item in items)
GUILayout.Label(string.Format("[{0}] {1}", item.Collected ? "" + item.password: " ", item.name ));
//GUILayout.Label("Take code to KeyPad");
}
}
GUILayout.EndVertical();
}
GUILayout.EndArea();
//Enter Code to unlock doors.
if (GUI.Button (new Rect (250, 830, 100, 50), "Enter Code"))
if (numItemsCollected > items.Count) {
Debug.Log ("Entering Code");
}
}
#endregion
#region Private
private int numItemsCollected;
#endregion
}
[System.Serializable]
public class Item
{
public string name;
//public GUIText textObject;
public GameObject gameObject;
public float guiDelay = 0.1f;
private float lastHoverTime = -99.0f;
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);
}
void OnMouseEnter ()
{
lastHoverTime = Time.timeSinceLevelLoad;
}
void OnGUI(){
if (lastHoverTime + guiDelay > Time.timeSinceLevelLoad) {
GUI.Box (new Rect (300, 300, 170, 250), name);
}
}
}
Attach following script to every item. It will show QUI.Box when mouse hovers the item. Just change the size of the QUI.Box and set the message to suitable values.
using UnityEngine;
using System.Collections;
public class HoverGUI : MonoBehaviour {
public string message = "Foo Bar";
public float guiDelay = 0.1f;
private float lastHoverTime = -99.0f;
void OnMouseOver() {
lastHoverTime = Time.timeSinceLevelLoad;
}
void OnGUI(){
if(lastHoverTime + guiDelay > Time.timeSinceLevelLoad){
GUI.Box(new Rect(0,0,170,250),message);
}
}
}

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