- Home /
Place Inventory Into GUI Button Grid
--- EDIT --- (Illustration added)
Hello,
I'm working on a basic Inventory, so far, all it does is:
User selects a specific object from the scene (eg: knife), this is then added as a GUI button in the inventory.
All I'm struggling with is places the Items/GUI Buttons in a grid format. All that happens right now, is if I pick up a knife - a GUI button is added to the inventory, then if I pick up something else, that knife button is simply 'replaced' by that.
What I want, is:
User picks up item/object, this is then added to inventory as button. User then picks up another object, and a button is placed, say 50 pixels downward, and so on.
Here is an illustration to get an idea:
Script:
var knife : GameObject; static var knifeInventory : boolean = false;
var password : GameObject; static var passwordInventory : boolean = false;
private var inventoryPanel : boolean = false;
var selGridInt : int = -1; var objGroup = new Array(GameObject); var objItemNames = new Array(String);
function Update(){ if(knifeInventory == true){ knife.gameObject.SetActiveRecursively(false); selGridInt ++; objGroup[selGridInt] = knife; objItemNames[selGridInt] = "Knife"; } else { knife.gameObject.SetActiveRecursively(true); }
if(passwordInventory == true){
password.gameObject.SetActiveRecursively(false);
selGridInt ++;
objGroup[selGridInt] = password;
objItemNames[selGridInt] = "Password";
}
else{
password.gameObject.SetActiveRecursively(true);
}
}
function OnGUI(){ if(Input.GetButton("Jump")){ if(inventoryPanel == false){ if(GUI.Button(Rect(10,70,120,30),"Show Inventory")){ inventoryPanel = true; }
} else if(inventoryPanel == true){ if(GUI.Button(Rect(10,70,120,30),"Hide Inventory")){ inventoryPanel = false; }
} if(inventoryPanel == true){ //selGridInt = GUI.SelectionGrid (Rect (10, 120, 120, 400), selGridInt, objItemNames, 1); GUI.BeginGroup (new Rect (10, 120, 120, 400)); GUI.Box(Rect(0,0,120,400),""); GUI.Button(Rect(0,10,120,30), objItemNames[selGridInt]); GUI.EndGroup(); }
}
}
Answer by Peter G · Feb 27, 2011 at 05:07 PM
EDIT: After reading your edit, I think the Dictionary option is better suited for your needs. And the first code block should work without any problems. I got a harmless error when running, but it shouldn't be a problem.
First of all, you cannot create an array like this: new Array(GameObject)
. That isn't a possible argument. Logically, you want to store an object with a name so a dictionary makes the most sense because it associates a value (in this case a reference) with a Key such as its name. So I decided to do this thing called Debugging to find the problems with the script I posted earlier and it turns out that it works splendid. Go figure :). So I fixed the bugs (why it is string
in C# and String
in js is beyond me) and now it should work fine.
public var items = new System.Collections.Generic.Dictionary.<String,GameObject>(); //Create the dictionary full of a name and a GameObject associated with it. private var curItem : System.Collections.Generic.KeyValuePair.<String, GameObject>; //This will store the current item.
public var buttonHeight = 50; public var width = 100;
function AddItemToInventory (item : GameObject) { items.Add(item.name, item); }
function OnGUI() { var curIndex = 0;
GUI.BeginGroup (new Rect (10, 120, 120, 400));
for(var item : System.Collections.Generic.KeyValuePair.<String, GameObject> in items) {
//iterate through all the items in the dictionary.
var rect : Rect = new Rect(0, buttonHeight * curIndex, width, buttonHeight);
//Create a rect for the button. It is offset more for each item in the array.
if(GUI.Button(rect, item.Key)) {
curItem = item;
//USE ITEM
RemoveItem(item);
//if the user clicks the button, select it as the current item.
}
curIndex++;
//increase the offset counter.
}
GUI.EndGroup();
}
//I added this function because I was getting an error deleting the KeyValuePair during the iterator function RemoveItem (item : System.Collections.Generic.KeyValuePair.<String, GameObject>) { yield WaitForEndOfFrame; items.Remove(item.Key); } //This code was in the comments, but I moved it up here so that everyone could see it.
function RemoveItem (item : GameObject) { yield WaitForEndOfFrame; items.Remove(item.name); }
This script should do exactly what you need. Call AddItemToInventory()
to add an item. Then in the GUI, the script iterates through the dictionary and for each item it does the following:
Create a rectangle that will be our final box. Offset each time so that it isn't on top of the one before it.
Create the button. It simply displays the name of the item in the box.
If the user clicks the button, do whatever you want with it, then remove it from the Dictionary.
Then it won't show up in the list anymore. And all the items will move up accordingly.
I figured I leave the list up since I had originally posted it and someone might find it useful. This one creates 2 lists, one storing the object and another storing its name which is a strange design concept, but it would get the job done. It displays a GUI toolbar that will display all the names of objects in the list. But you would then have to do something accordingly and I don't think this way will do what you want as well as the other.
public var items : System.Collections.Generic.List.<GameObject>(); //Create a list of all the items in our inventory
private var itemNames : System.Collections.Generic.List.<string>(); //Create a list of all their names for the GUI.
public var curItemID : int; //Use this to find the item in your array.
function AddItemToInventory (item : GameObject) { items.Add(item); itemNames.Add(item.name); //fixed this line. }
function OnGUI() {
GUI.BeginGroup (new Rect (10, 120, 120, 400));
curItemID = GUI.Toolbar(someScreenRect, curItemID itemNames.ToArray() );
//This will automatically add another box for each item you have
GUI.EndGroup();
}
Thanks for your help - but I'm getting a lot of errors, such as missing ; and (). I tried fixing most of it, Also get a "Name is not a member of UnityEngine.GameObject". Also "$$anonymous$$ identifier: Dictionary & $$anonymous$$eyValuePair. Thanks
that name should be lowercase...name. 2. AFAI$$anonymous$$ you can't use "using" in js. but I'm lazy and that is a lot to write. Try pasting that before all the generic objects, thats just dictionary and $$anonymous$$eyValuePair
Peter's script seems to be in C#, Oliver, make sure you haven't saved it as a .js, as I think that'll confuse Unity.
Believe it or not it is written in js. Unity3 added support for generic collections in javascript.
Thanks Peter - I've given the first script a go, and I came across a few problems: When I pick up my first object (knife) - the button appears (good!), but when I then go and pick up a few more objects, their buttons do not appear until I click on the knife button. Its as if the knife button acts like a toggle on and off button for the rest of the buttons. The knife button does not disappear when I click on it - as for the rest, they work fine, until I click on the knife button, then everything is reset. Hope that makes sense.
Answer by Scribe · Mar 01, 2011 at 09:06 PM
Don't know if this helps or not:
var inventory : Array; public var emptyTex : Texture; var iconWidthHeight = 20;
// Create the an 8x8 Texture-Array function Awake() { inventory = new Array(8); for( var i = 0; i < inventory.length; i ++ ) { inventory[i] = new Array(8); } }
function OnGUI() { var texToUse : Texture;
//Go through each row
for( var i = 0; i < inventory.length; i ++ )
{
// and each column
for( var k = 0; k < inventory[i].length; k ++ )
{
texToUse = emptyTex;
//if there is a texture in the i-th row and the k-th column, draw it
if( inventory[i][k] != null )
{
texToUse = inventory[i][k];
}
GUI.Label( new Rect( k*iconWidthHeight, i*iconWidthHeight, iconWidthHeight, iconWidthHeight ), texToUse );
}
}
}
Not that great at arrays myself and this script is not mine I found it here: http://forum.unity3d.com/threads/11167-Laying-out-Icons-In-an-inventory-that-will-rearrange-to-fit
hope this helps you
Scribe
these two might also help: the first makes use of the script in my previous post: http://forum.unity3d.com/threads/49202-Inventory-amp-Looting-based-on-Forum-Code?p=312325 and the other is another inventory system not really what your looking for in this answer but if you are making an inventory system then it might be useful later: http://forum.unity3d.com/threads/48290-just-another-inventory
Answer by Flynn · Mar 02, 2011 at 07:26 PM
The GUI gets cleared every time OnGUI gets called, so any previous Button calls you made are lost forever. This is why your boxes appear to disappear -- your rendering focus changes as the selection goes up. OnGUI also gets called every render update, instead of every change. (As opposed to Flash, which remembers what you render, and can be configured to only update when you make changes to the UI). This is kind of inefficient but has it's benefits in the long run.
So, how can you accomplish what you want?
Say you have an array of strings that you want to draw in a grid. (Doing strings for simplicity, you can change it to whatever you want, and have the handling be slightly different.)
This code would render those as selectable buttons in a grid: (As testing, I suggest copying and pasting then tire code into a new script, and adding that script to an object, then running, to see how it runs)
Note that button "0" will be invisible as it is initially selected.
//Depending upon what array type you use, slightly different solutions may be needed. //I'll go ahead and use a normal JS array. var arrayOfStrings = new Array(); var selected = 0;//This is not used how you use it. It is the player-decided selection.
function Start() { //Initialize our vars. selected = 0; arrayOfStrings = new Array();
//Add a whole bunch of strings from 0-50.
for (var i = 0; i < 50; i++)
{
arrayOfStrings.Add("" + i);
}
//Remove the 13th because it's unlucky.
arrayOfStrings.RemoveAt(13);//All indexes still start at 0, but we can do 13 because we have the number "0" in the array.
//Remove the last one because he slept in today.
arrayOfStrings.RemoveAt(arrayOfStrings.length - 1);
//Remove 30 because he marks the age you become old.
//We don't want to become old.
arrayOfStrings.Remove("30");
}
function OnGUI() { selected = DrawStringArray(arrayOfStrings, 7, new Vector2(50,25), selected);//Call oru helper function! :)
}
//Call this in OnGUI when you want your buttons to be displayed. function DrawStringArray(arr, rowLength, itemSize, sel) { //arr is an Array object //Row Length is how many items are displayed in a row. //itemSize is how large an item is. //sel is the currently selected item, and is not necissarry for basic function.
for (var i = 0; i < arr.length; i++)
{
//First, calculate the XY position.
var x = 0;
var y = 0;
x = i%rowLength;//This basically loops the number back to 0 when it hits rowLength. (It's the remainder operator)
y = Mathf.Floor(i/rowLength);//This means that every time i hits rowLength, y will be one greater. We Mathf.Floor it to make sure any decimal pointi s truncated -- Thereby making it snap, and not smoothly go down.
var rect = new Rect(x*itemSize.x, y*itemSize.y, itemSize.x, itemSize.y);
//From here on, you can do whatever you want, but I will continue programming just ot give an example
if (sel == i)//We ae about to draw the selected one.
{
//DoSomethingSpecialWhichIAmNotCreativeEnoughToDoMySelf
}
else
{
sel = (GUI.Button(rect,arr[i])?i:sel);//This is an interesting way of do it. It is equivelant to:
/*
if (GUI.Button(rect,arr[i]) == true)
{
sel = i;
}
else
{
sel = sel;
}
*/
}
}
return sel;
}
Your answer
Follow this Question
Related Questions
GUI grid of buttons issue. 1 Answer
UI: Dynamically sized inventory window with scroll 0 Answers
How to select a texture in an inventory? 0 Answers
Instantiating object from inventory 0 Answers
Mysterious crash involving an array 2 Answers