- Home /
How do I 'use an item'?
So, I have a very nice inventory GUi set up, and I can take GameObjects with an item script attached to them, and pick them up and put them in the inventory. When I right click an item, what should happen is the item is 'used'. I've got it recording these clicks... but I have no idea how to make the item 'go'.
I believe I need two types of items. A instant use item (potions say) and delayed use items (like a grenade, that brings up a targeting cursor and isn't actually used until thrown)
Unfortunately, I'm banging my head against the keyboard trying to figure out a clean way to do this without a bunch of delegate calls just to use one item, and might end up mis-triggering if the player is mashing the mouse.
How can I trigger the effects of an item. And if the item is used successfully, consume/destroy the item? Is there some way to just call a 'UseItem' function and pass it some details on what to do?
This is InventoryController which manages incoming items and outgoing items. It subscribes to a event 'slotClickedEvent' which is triggered by IPointerDown on a Slot (which is just a button)
using UnityEngine;
using System.Collections;
using System;
using UnityEngine.UI;
//Player checks inventory to see what they are carrying
//regarding passing of "Items" around.
//We HOLD and PASS GameObjects
//We ACCESS the Item component to do Item-y things.
public class InventoryController : MonoBehaviour
{
//...bunch of graphical related vars here...
private GameObject objectInHand //used with some accessors to track if mouse is holding an item. Mostly related to click/drag moving items between slots
private ArrayList allSlots;
private int emptySlots;
void Start()
{
//...
EventManager.slotClickedEvent += SlotClicked;
}
//code for picking up and adding is item here
private void SlotClicked(Slot slot)
{
if (Input.GetMouseButtonDown(0))
{
//logic for picking up and placing items here
}
else if (Input.GetMouseButtonDown(1) && slot.hasUseableItem)
{
////////////////////////////////
//use item code would go here//
}
}
//...code for tracking empty slots, adding items from the world etc here...
}
This is Slot, which is attached to a GUI prefab (has an image attached to another image)
//public delegate void SlotClickEventDelegate(Slot slot);
//public static event SlotClickEventDelegate SlotClickEvent;
private GameObject objectInSlot = null;
private Item itemInSlot = null;
public Item item
{ get { return itemInSlot; } }
// Use this for initialization
void Start ()
{ //...
}
//...methods for adding/merging/swapping items here...
public GameObject RemoveItem()
{
GameObject obj = objectInSlot;
objectInSlot = null;
SetSprite(null, 0.0f);
itemInSlot = null;
UpdateStackText(0);
return obj;
}
public void OnPointerDown (PointerEventData data)
{
EventManager.SendSlotClickEvent(this);
}
}
This is Item, which is attached to a GameObject meant to be used as.. an Item.
public class Item : MonoBehaviour {
public ItemType type;
public Sprite icon;
//public string name;
public void Use()
{
}
}
Impossible to answer without actually knowing what your code is. There's no one way to do an inventory system. Do you use a library? Perhaps an array? Or a list? Do you use buttons to display the items?
Even if we wish to be, we're not psychic.
Ok, I pasted in parts of the script I'm using (there's a lot of ad$$anonymous$$istrative stuff that would pad this out to several screens if left in. Those just handles click/drag/drop of icons in the inventory mostly.) I think this is enough to get the gist of things behind clicking and using an item.
It starts with clicking a Slot prefab, which sends itself via event to InventoryController, which then makes a decision based on what key was pressed. In this case, $$anonymous$$ouse 1 for use whats in the Slot passed. Slot allows access to the item.
Answer by Zarenityx · Feb 18, 2015 at 09:54 PM
Personally, this is not how I would go about making an inventory system. From what I could understand your items are actually GameObjects, and each Item's specific behavior is controlled by the value of an enum. The problem here is that accessing the item component can be slow, and all of the item's functions and attributes have to be in the item component, which can be tedious to make and doesn't easily allow for the creation of different types of items.
What I would do is essentially separate the Item from the Object. In my inventory systems, I make a class (one that does NOT inherit from MonoBehavior) called Item, where I store the basic item data, such as the name, amount, ID, GUI image/mesh, etc, and a few virtual functions, such as Use(), Equip(), Unequip(), etc.
Once you have the basic Item class, then you can start making your actual items by making other classes that inherit from Item, and override the functions and variables. This is where #region directives really come in handy.
Then make a 'real' script (a monobehavior one), to manage the inventory and call the appropriate functions when something happens, such as calling Use() when clicked.
Finally, you want a way to make the items and the objects work together, so a very simple script can be made such that when the object is interacted with, it looks up the actual Item you are talking about (I would create a static class with a list of all possible items), gives you that item/ increases it's quantity, and then gets rid of the actual object.
As for grenades vs potions, that change is easily implemented by changing whether or not you Equip() or Use() the item when you call your "clicked" function.
It might seem like extra work to start out with, but do you really want to be stuck with a bunch of ifs or switch-cases, and then extra work to save the inventory to a file when you end up with hundreds of items? Plus, you could easily take these scripts, make them into a UnityPackage, and use them in other projects with only a few modifications, and if you can already make an array of items display as a GUI, organized in stacks, the conversion of the GUI system should be trivial.
I hope this helps! I understand that scrapping most of a framework can be hard, but it is definitely worth it to have an optimized and polymorphic system for things like this- believe me, I have tried and failed at least 5 or so ways to make a voxel engine, and the one I have now is at a point where I may need to start over again, but that's just part of the learning process, I guess.
That sounds like a solid plan.
When I want to do the effects of a Use, say, heal the player - should I be doing some sort of look up (like GameObject.FindWithTag<>()? Is there a better way to grab disparate objects to call functions on them?)
You can create a script for instancing references to gameobjects. It's as simple as making a gameobject list if you wish to go this way.
Also, if you're going to use delegates, i warmly recommend learning the lambda expression (=>), it's much faster than a delegate, and requires less code.
Not sure I understand how I would use lambda to simplify making an event.
Presently, to make an event that other objects can subscribe to, I do this
public delegate void PickupItemDelegate(GameObject itemObj);
public static event PickupItemDelegate pickupItemEvent;
public static void SendPickupItemEvent(GameObject itemObj)
{
pickupItemEvent(itemObj);
}
which I got from Unity's tutorial on events. I stick a bunch of these into a class I call Event$$anonymous$$anager and just have one object subscribe a function to an event, and another object call the corresponding 'SendXXXXX'
Is there some way Lambda can be used to simplify this big block of code? Googling "c# lambda events" turned up a way to subscribe to these events with a lambda function, but nothing on simplifying the above.
Just a little tip...
if you change the event line to
public static event PickupItemDelegate pickupItemEvent = delegate {};
You don't have to worry about checking if pickupItemEvent is null, or throwing an exception if nothing is subscribed to it.
Also... I wouldn't subscribe anonymous functions (declared with lambdas) to this kind of event because the lack of a name make removing them impossible, which is totally non-optimal.
Lamdas are better for when you have a method that takes a delegate as a parameter.... like for an oncomplete
public delegate void OnCompleteDelegate ();
public void actThenCallOnComplete( string someWords, OnCompleteDelegate callOnComplete)
{
Debug.Log(someWords);
callOnComplete();
}
void Start()
{
actThenCallOnComplete( "some text" , () => { Debug.Log(" some more text."); } );
}
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Invoking Effects of (Random) Items (C#) 0 Answers