- Home /
How to Pick up 1 collider Object at a time, Vs. all at once? Item Script
Having trouble with picking up multiple items at the same time, and as well, weight implementation within my item script. The script worked perfectly fine until I added a weight limitation. Sometimes it works, sometimes it doesnt. Sometimes it allows me to pick up more than the max bag weight threshold, especially if I spam the pick up key. But others, it seesm to work fine? Its very weird..
Code:
// Variables:
public int itemAmt;
public bool dropped = false;
private static int bagAmmo;
private bool hasBag;
[SerializeField] public int fullAmt;
[SerializeField] public bool isItemFull;
[SerializeField] private Inventory inventory;
[SerializeField] public int lowRandAmt;
[SerializeField] public int highRandAmt;
[SerializeField] AudioSource itemPickupSound;
[SerializeField] public string itemNameInput;
[SerializeField] public static int currentTotalWeight;
public int maxBagWeight;
void Update()
{
Debug.Log("hitting update: current weight: "+ currentTotalWeight);
}
public void OnTriggerStay(Collider other)
{
// check if its the player:
if(other.tag == "Player" && !PlayerBehavior.bagFull)
{ //Reset CUrrent Total Weight:
currentTotalWeight = 0;
// Grab global bag variables:
hasBag = PlayerBehavior.hasBag;
maxBagWeight = PlayerBehavior.maxBagWeight;
// Check to see if user hits the "E" key:
if(Input.GetKeyDown(KeyCode.E) && hasBag)
{
// Reset Current Weight And Bag empty bool:
if(PlayerBehavior.inv.GetItemList().Count == 0) // If inventory is empty, no point in slowing things down with an uneccessarry for loop.
{
PlayerBehavior.bagFull = false;
}
else{
foreach(Item item in PlayerBehavior.inv.GetItemList())
{
if(item.itemType.ToString() == "Money")
{
currentTotalWeight += item.amount; // Money weight is the same as amt. aka 1 each.
}
else{
currentTotalWeight += item.weight;
}
}
if(currentTotalWeight > 0 && currentTotalWeight < maxBagWeight)
{
PlayerBehavior.bagFull = false;
}
}
// Assign Item Amount Functionality: ----------------------------
// This is just someting for when IM placing the item, I have checks that make it have these amounts at start up depending. These are Serialized Variables I change from within Unity to determine the values of the item.
// Check if the clip is full: If so, then skip randomization:
if(!isItemFull && !dropped && itemAmt == 0){
// Creating a random amount of ammo amount between low and high to put inside the clip. Otherwise override with full Amount size:
itemAmt = Random.Range(lowRandAmt, highRandAmt);
}
else if(isItemFull && !dropped){
itemAmt = fullAmt;
isItemFull = false;
}
else{
itemAmt = itemAmt;
}
Debug.Log("Current Total Weight Under Item Script: "+ currentTotalWeight);
Debug.Log("Current Total max bag weight Under Item Script: "+ maxBagWeight);
if(currentTotalWeight < maxBagWeight)
{
int leftOver = maxBagWeight - currentTotalWeight;
int newWeight;
int newAmt;
// If Glock Handgun:
if(itemNameInput == "Glock_Handgun"){ // NOTE HAND GUN IS HAVING HAVING ISSUES WITH FLOATING SO TURN ON GRAVITY IF ITS A HANDGUN!
newWeight = currentTotalWeight + 5000;
if((newWeight > 0 && newWeight < maxBagWeight)){
// Debug.Log("hitting under add pistol glock les than bag weight");
PlayerBehavior.inv.AddItem(new Item {
itemType = Item.ItemType.Glock_Handgun, amount = itemAmt, weight = 5000
});
// Play pickup sound:
itemPickupSound.Play();
Destroy(this.gameObject);
}else{
Debug.Log("Cant fit pistol");
}
}
// If PistolAmmo:
if(itemNameInput == "Pistol_Ammo"){
newWeight = currentTotalWeight + 500;
if((newWeight > 0 && newWeight < maxBagWeight)){
PlayerBehavior.inv.AddItem(new Item {
itemType = Item.ItemType.Pistol_Ammo, amount = itemAmt, weight = 500
});
// Play pickup sound:
itemPickupSound.Play();
Destroy(this.gameObject);
}else{
Debug.Log("Cant fit Ammo");
}
}
}
else{
Debug.Log("Bag is full");
// itemPickupSound.Play();
}
}
}
}
EDIT: Here is my Inventory and item classes. The inventory is only a class, but the itemList is transfered to my player script called 'Player Behavior".
NOTE* The only thing that isnt attached to the player is pretty much the Item script. Which I plan on changing.
Item script:
public enum ItemType {
Glock_Handgun,
Money,
Pistol_Ammo,
Rifle_Ammo,
Sniper_Ammo,
Shotgun_Ammo
}
public ItemType itemType;
public int amount;
public int weight;
// Get Sprite Function:
public Sprite GetSprite() {
switch (itemType) {
default:
case ItemType.Glock_Handgun: return ItemAssets.Instance.glock_Handgun_Sprite;
case ItemType.Money: return ItemAssets.Instance.money_Sprite;
case ItemType.Pistol_Ammo: return ItemAssets.Instance.pistol_Ammo_Sprite;
}
}
public GameObject GetModel() {
switch (itemType) {
default:
case ItemType.Glock_Handgun: return ItemModelAssets.Instance.glock_Handgun_Model;
case ItemType.Money: return ItemModelAssets.Instance.money_Model;
case ItemType.Pistol_Ammo: return ItemModelAssets.Instance.pistol_Ammo_Model;
}
}
public bool IsStackable()
{
// True = stackable, False = unstackable:
switch(itemType)
{
default:
case ItemType.Money:
return true;
case ItemType.Glock_Handgun:
case ItemType.Pistol_Ammo:
return false;
}
}
Inventory Script:
public class Inventory {
public static List<Item> itemList;
public static Inventory Instance;
void Awake(){
Instance = this;
}
public Inventory() {
itemList = new List<Item>();
}
public void AddItem(Item item)
{
// Check if Stackable:
if(item.IsStackable())
{
// Check if this item type is inside the inventory:
bool itemAlreadyInInventory = false;
foreach (Item inventoryItem in itemList){
if(inventoryItem.itemType == item.itemType)
{
inventoryItem.amount += item.amount;
itemAlreadyInInventory = true;
}
}
if(!itemAlreadyInInventory){
itemList.Add(item);
}
}else{
itemList.Add(item);
}
PlayerBehavior.bagEmpty = false;
}
public List<Item> GetItemList()
{
return itemList;
}
}
@Hellium This is the new post.. But this one as well was put under moderation?? The heck???
Answer by Hellium · Sep 16, 2020 at 08:17 PM
Everything below is based on assumptions and from what I understood of your system, and based on the answer I gave you here. Everything here are suggestions and might not be compatible with your current system, but it can give you some ideas. The code has not been tested, I don't even know if it compiles!!
First of all, to make things clear:
There should be one inventory, which is a script attached to the player
Objects which can be picked should not even know the existence of the inventory
There should be another script responsible for picking the objects, also attached to the player.
Then, I am not convinced the amount
should be part of the Item
class itself. From my point of view, Item
should be a class containing data about the item itself and nothing more. The inventory & the pickable items can manage the amount of items you have and the amount to pick.
Item class
Will give you the possibility to create Scriptable Objects for each of your items
[CreateAssetMenu()]
public class Item : ScriptableObject
{
[SerializeField] private new string name; // The `new` here is to "override" the name variable in the base class
[SerializeField] private Sprite sprite; // You will be able to get rid of your ItemAssets & ItemModelAssets classes
[SerializeField] private GameObject model;
[SerializeField] private bool isStackable;
public string Name { get { return name; } }
public Sprite Sprite { get { return sprite; } }
public GameObject Model { get { return model; } }
public bool IsStackable { get { return isStackable; } }
// private variable + public getters to make sure nobody but the editor changes the values
}
// You can also create subclasses for your gun for instance if you want to specify the size of the magazine
Inventory class
To be attached on the player GameObject
public class Inventory : MonoBehaviour, IEnumerable<InventoryItem>
{
[SerializeField, Min(1)] private int maxWeight = 5000;
private List<InventoryItem> inventoryItems = new List<InventoryItem>();
public int Weight { get; private set; }
public int MaxWeight { get { return maxWeight; } }
public bool IsEmpty { get { return Weight == 0; } }
public void Add(Item item, int quantity, out int quantityAdded)
{
InventoryItem inventoryItem = item.IsStackable
? GetInventoryItem(item.itemType)
: null;
if(inventoryItem == null) // No items of this type present in the bag, or non stackable, add it
{
inventoryItem = new InventoryItem{ Item = item, Amount = 0 };
inventoryItems.Add(inventoryItem);
}
// Limit the quantity if needed
if(item.IsStackable == false)
quantity = 1;
StackItem(inventoryItem, quantity, quantityAdded);
// I would have done the opposite, the player behavior should check whether its inventory is empty or not
// See `IsEmpty` getter
PlayerBehavior.bagEmpty = false;
}
private void StackItem(InventoryItem inventoryItem, int quantity, out int quantityAdded)
{
for(quantityAdded = 0 ; Weight + inventoryItem.Item.weight < MaxWeight && quantityAdded < quantity ; ++quantityAdded)
{
inventoryItem.Amount++;
Weight += inventoryItem.Item.weight;
}
}
public InventoryItem GetInventoryItem(Item.ItemType itemType)
{
foreach(var inventoryItem in inventoryItems)
{
if(inventoryItem.Item.itemType == itemType)
return inventoryItem;
}
return null;
}
// Will allow you to call
// foreach(InventoryItem inventoryItem in inventory)
// // Do something
public IEnumerator<InventoryItem> GetEnumerator()
{
return inventoryItems.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class InventoryItem
{
public Item Item;
public int Amount;
}
}
PickableItem class
PickableItem class
public class PickableItem : MonoBehaviour
{
// Fill in the inspector, set minAmount & maxAmount to same value
// if you want a fixed amount, e.g 1 & 1 to add 1 item in the inventory
[SerializeField] private int minAmount;
[SerializeField] private int maxAmount;
[SerializeField] private Item item; // Drag & drop the item ScriptableObject
public int Amount { get; set; }
private void Awake()
{
Amount = Random.Range(minAmount, maxAmount);
}
public Item GetInventoryItem()
{
return item;
}
}
ItemPickup class
To be attached on the player
public class ItemPickup : MonoBehaviour
{
private List<PickableItem> itemsInRange = new List<PickableItem>();
[SerializeField]
private AudioSource itemPickupAudioSource;
[SerializeField]
private Inventory inventory;
private void Update()
{
if(Input.GetKeyDown(KeyCode.E) && itemsInRange.Count > 0)
{
PickableItem pickableItem = FindPickableItemClosestToView();
if(pickableItem != null)
AddItemToInventory(pickableItem);
}
}
private void AddItemToInventory(PickableItem pickableItem)
{
Item item = pickableItem.GetInventoryItem();
int quantityAdded;
inventory.Add(item, pickableItem.Amount, out quantityAdded);
if(quantityAdded > 0)
itemPickupAudioSource.Play();
// The condition is optional
// It's just if you want to keep the item on the ground if
// you did not picked the whole pile
if(quantityAdded == pickableItem.Amount)
Destroy(item.gameObject);
else
pickableItem.Amount -= quantityAdded;
}
private PickableItem FindPickableItemClosestToView()
{
PickableItem itemToPick = null;
float bestDotProduct = -1;
foreach(PickableItem item in itemsInRange)
{
// https://docs.unity3d.com/462/Documentation/Manual/UnderstandingVectorArithmetic.html
// Check whether the player is almost looking at the item
float dot = Vector3.Dot(transform.forward, item.transform.position - transform.position);
if(dot < bestDotProduct && dot > 0.866f)
{
itemToPick = item;
bestDotProduct = dot;
}
}
return itemToPick;
}
private void OnTriggerEnter(Collider other)
{
PickableItem pickableItem = other.GetComponent<PickableItem>();
if(pickableItem != null)
{
itemsInRange.Add(pickableItem);
}
}
private void OnTriggerExit(Collider other)
{
PickableItem pickableItem = other.GetComponent<PickableItem>();
if(pickableItem != null)
{
itemsInRange.Remove(pickableItem);
}
}
}
Wow.... This is very complex stuff! Thanks man! Regardless of the current response and updates to the posts showing you more of my code, this I think will get me on the proper tack. I think my approach was all wrong.. And I may very well have to scrpa and start it over lol. however, until then, check out the other puzzle pices "$$anonymous$$y item and inventory class" Let me know what you think as that may shine a but more understanding to my approach here. Aside from that, thank you very much! You have helped me more in the past few days than the past week of my self taught development lol!
I'm glad it helped you. I've added your Inventory & item script to your original question. I can't update my answer right now but I definitely will in the next 24 hours.
I believe ScriptableObjects will be really useful in your situation. But yes, I fear my suggestions will lead to a complete rework of your system. For the best I hope :D
For sure! I actually originally tried to do scriptable object for the items, but it seemed I wasnt able to achieve my goal.. $$anonymous$$aybe im just a turd and possibly implementing it wrong.
I unfortunately havent be able to see your (more updated) response, but hopefully itll show up tomorrow lol. I eagerly await your current update.
Looking at this as a whole, I am afraid I will need to scrap it all... Seriously worried as that is legit about a week or 2 of my trial and error lol! Sucks to start from scratch... I$$anonymous$$ going to try and reverse engineer a bit more until I scrap it. But nonetheless, thanks again man! This is TRULY insightful!
I've updated my answer.
Again, I have not tested it at all, I did not even write this in a proper C# IDE, so some fixes might be needed.
I don't want you to start from scratch but now, you have an idea of how I would have done this, pick the things you want from this answer (pun intended).
Cheers
Your answer
Follow this Question
Related Questions
Check bool from multiple instances of the same script 2 Answers
[Oculus SDK] How to add colliders to OVRGrabbable at runtime? 2 Answers
Spawning an object on the opposite side of an activator object with no overlaps 1 Answer
OnCollisionEnter for other gameobjects (C#) 2 Answers
Check bool from multiple instances of the same script 0 Answers