Hi all, this was actually a part of my turnbased rpg prototype. Now when i know more about unity and c# i am rewritting whole thing, but made a rip of my inventory system. It contains a sample of integrated items database editor with saving to binary file and example of game inventory window. To use - unzip archive, load project, load 'test' scene, hit play. "I" key - brings up inventory menu, Q,W,E,R,T - add different items to inventory. Controls a similar to Dragon Age inventory, drag and drop items from inventory to slots and back, right mouse click on inventory item brings up item menu, where you can equip or destroy item. Note that this rpg project was started when i know little about programming, so there can be done a lot of optimisation, and a lot can be done better (what i actually doing right now ) but its works and i hope will be usefull to someone. P.S. Sorry for bad english in message and project commentary, but i think for unity community its better bad english than good russian
Wow! Thanks for this. I noticed you also threw in many more of you scripts like the player controller, game controller, message system etc. Thanks alot!
stneas: I'm not getting this to work for me. UnityPro 2.6.1f3 on the Mac I downloaded your project, loaded the scene test.unity from the project inventory. I hit play and type "i". I get the four buttons across the top, and the three empty slots for arms and armor. If I click on one of the empty slots, I get a screen full of NullRef! errors. The main error I'm getting on load is "No weapons file Found" from InventoryControl.cs and this seems to stop the loading process. There is a file called weapons.dat in the inventory folder outside the Assets folder. I did follow the logic a little, and InventoryControl.cs calls BinarySaver.Load(GlobalSettings.File_Weapons), and this is defined in GlobalSettings.cs as public static string File_Weapons = ".\\weapons.dat"; which is presumably correct... Any thoughts? Mac/PC thing? I'm also getting a small number of warnings that someVarible is assigned a value but that value is never used, but I think this is irrelevant to the issue. Anyone else having problems?
Well... I've tracked the problem down to ( as I suspected ) loading the weapons.dat. I assume that this could be the same problem with the other .dat files. What I am not experienced with is all the serializable work and the .load ( and this doesn't seem to be resources.load ) that's being used. It's pointing to a string called .\\filename.dat ( and I've tried screwing with this to no avail ). What does .\\ indicate? Seems like it's trying to indicate a directory location?? What scares me is that this seems to be working for other folks out of the box, but not me.
Little Angel and all mac users, just get back home from work and try it on mac, yes seems the problem is in the ".\\" chars at the beginning of file names, in windows they represents current application directory and don't work on mac. Just removed then and left "weapons.dat" , "potions.dat", "armor.dat" and its starts working. About not used variables, as i said this was a part of my game prototype, i try to clear all unnecessary classes, methods and variables, but some might still stay.
Very nice. Works on the Mac when .\\ is removed. I see there is still some distance to go before it is finished. stneas: If you develop this, you can get revenue. Look at Aron Granberg's license for his A* pathing (www.arongranberg.com/unity/a-pathfinding) where the license states that you must pay if you are using the work in a commercial game. I, for one, would pay a reasonable fee along the lines of the fee for Aron's pathing system for a simple working inventory system if I were to use it in a commercial game.
Cool! Thanks so much for this! Exactly what I was looking for the other day. The side menu in the screenshot isn't there though. The one that says Item window, equip, destroy, cancel.
Just an FYI for those fooling 'round with this: The editor scripts for making items are not currently trapped, so if you hit "save database" and you have not yet loaded one, you will over-write your existing items database with a blank one. This is a bit cheap, but I put of a trap around the save button like this (from ArmorEditor.cs): Code (csharp): private bool wantToSave = false; void OnGUI() { GUILayout.BeginHorizontal(); if (!wantToSave) { // Load Database if (GUILayout.Button("Load database", GUILayout.Width(128))) { armor.Clear(); Hashtable toLoad = BinarySaver.Load(GlobalSettings.File_Armor) as Hashtable; if (toLoad == null) { Debug.Log("No armor file found"); return; } ICollection armorCollection = toLoad.Values; foreach (ArmorSaver listArmor in armorCollection) { Armor loadArmor = (Armor)listArmor; armor.Add(loadArmor); } } // SAVE Database if (GUILayout.Button("Save database", GUILayout.Width(128))) { wantToSave = true; } } if (wantToSave) { // REALLY SAVE Database if (GUILayout.Button("SAVE DATABASE", GUILayout.Width(128))) { Hashtable toSave = new Hashtable(); foreach (Armor listArmor in armor) { ArmorSaver saveItem = (ArmorSaver)listArmor; if (!toSave.ContainsKey(saveItem.name)) toSave.Add(saveItem.name, saveItem); } BinarySaver.Save(toSave, GlobalSettings.File_Armor); wantToSave = false; } // Cancel the save if (GUILayout.Button("CANCEL", GUILayout.Width(128))) { wantToSave = false; } } GUILayout.EndHorizontal(); // Please continue with the existing code This just protects the save button with a required second click and changes the button position to prevent accidental double clicks.
This also seemed to remove the chance of unseemly headaches. Code (csharp): void OnEnable () { LoadDatabase(); } void LoadDatabase() { armor.Clear(); Hashtable toLoad = BinarySaver.Load(GlobalSettings.File_Armor) as Hashtable; if (toLoad == null) { Debug.Log("No armor file found"); return; } ICollection armorCollection = toLoad.Values; foreach (ArmorSaver listArmor in armorCollection) { Armor loadArmor = (Armor)listArmor; armor.Add(loadArmor); } } I've not thought the logic all the way through, but I can't seem to think of a reason not to load the database when enabling the editor script. This is just rearranging the code that was previously associated with the load button. I've also done this: Code (csharp): // Load Database if (GUILayout.Button("Re-Load database", GUILayout.Width(128))) { LoadDatabase(); }
Wow, looks good. I hope this can save me some time for save/loads, but I dont need any invatory, so I can just get rid of that.. but thanks, you may have just saved some people some time
When I try to Export Package, it creates just a small file, not including most of the stuff, and then when I try to Import Package into another game I'm working on, it fails, claiming its because of the database. Any ideas why that happens? I could just copy everything over by hand I suppose. Just curious why that error would exist.
Well, If you look at the scripts the save/load script wasn't made by him but was borrowed from some guy in the unity3d.ru forums.
Isn't that what's so great about the Unity community? People post the work they've done and others improve on it and post it back. Just imagine how much work this would be if everyone had to make everything on their own. I work as a "one man band" right now, and the sharing and cooperation from the community was one of main reasons I chose Unity over the competition - and I'm so glad I did. Now I only wish I knew more languages - like Russian! So I could post my work and read the forums in other languages.
aksdad : i'am not making big secret from that i borrowed the script, author commentary left untouched , for those who interested i also added to saveload system AllowAllVersionDeserializationBinder class from this article http://spazzarama.wordpress.com/2009/06/25/binary-deserialize-unable-to-find-assembly/ this was some serious pain to figure out why unity cannot load binary file when i making changes to inventory item classes, while xml files don't have this problem. Well at least for me this was serious pain Little Angel: yes, code is not foolproof , actually i use a little different save-load button methods using EditorUtility.OpenFilePanel and EditorUtility.SaveFilePanel. For example for load you can change code to : Code (csharp): if (GUILayout.Button("Load database", GUILayout.Width(128))) { fileName = EditorUtility.OpenFilePanel("Select armor db file", "", "dat"); if (fileName.Length != 0) { armor.Clear(); Hashtable toLoad = BinarySaver.Load(fileName) as Hashtable; if (toLoad == null) { Debug.Log("Error loading file"); return; } ICollection armorCollection = toLoad.Values; foreach (ArmorSaver listArmor in armorCollection) { Armor loadArmor = (Armor)listArmor; armor.Add(loadArmor); } } else Debug.Log("Error loading file"); } and have 'save' and 'save as' button, thus allowing saving a few versions of inventory db. Just wanted to keep code as simple as possible.
Very nice! I'll have to look into that when I'm back at my desk. (I usually don't get time with Unity on Tue Wed.) And stneas: No criticism of code, especially code that is "work in progress". I just wanted to pass what I had discovered and post my solution code to the other people watching this thread. It took me a few time of deleting the database to figure out how I did it, so that was just to let people know in case they had done it as well.
Little Angel: it's allready there , just want to add don't miss it if you just want add binary saveload script to your project, AllowAllVersionDeserializationBinder.cs also is in the SaveSystem, and it's used in load method of BinarySaver.cs Code (csharp): BinaryFormatter formatter = new BinaryFormatter(); formatter.Binder = new AllowAllVersionDeserializationBinder(); obj = (object)formatter.Deserialize(stream); Another solution probably will be to have all your serialised save classes packed to external dll, but that not convenient to me as using custom SerializationBinder;
Code (csharp): Assets/Scripts/Items/Weapon.cs(50,36): error CS0117: `WeaponSaver' does not contain a definition for `weaponType' Any idea why I'd have that error? I didn't change that script at all, nor the weaponseditor.cs I didn't have it before. Now it won't run, that not there. Also, the menu in the Unity editor that say "INVENTORY" is no longer there.
Wolf Dreamer: Check that WeaponSaver.cs have this line Code (csharp): public WeaponType weaponType; //weapon type that error is as it's said emerges when class dosen't have definition for variable, or you could try restore WeaponSaver.cs from downloaded archive.
Code (csharp): using UnityEngine; public class Weapon : InventoryItem { public float minDamage; //weapon min-max damage public float maxDamage; // public GameObject projectile; //weapon projectile instaniate on fire public GameObject model; //weapon model public WeaponType weaponType; //weapon type public Weapon() { } public Weapon(Weapon copyItem) { name = copyItem.name; fullName = copyItem.fullName; description = copyItem.description; price = copyItem.price; icon = copyItem.icon; classRestriction = copyItem.classRestriction; stackable = copyItem.stackable; maxStack = copyItem.maxStack; reqStrength = copyItem.reqStrength; reqDexterity = copyItem.reqDexterity; reqIntellect = copyItem.reqIntellect; reqLevel = copyItem.reqLevel; minDamage = copyItem.minDamage; maxDamage = copyItem.maxDamage; projectile = copyItem.projectile; weaponType = copyItem.weaponType; model = copyItem.model; } public static explicit operator Weapon(WeaponSaver itemSave) { Weapon item = new Weapon(); item.name = itemSave.name; item.fullName = itemSave.fullName; item.description = itemSave.description; item.price = itemSave.price; if (itemSave.iconName != "") item.icon = (Texture2D)Resources.Load("ItemsIcon/" + itemSave.iconName); if (item.projectile != null) item.projectile = (GameObject)Resources.Load("Prefabs/Projectiles/" + itemSave.projectileName); if (item.model != null) item.projectile = (GameObject)Resources.Load("Prefabs/WeaponModels/" + itemSave.modelName); item.weaponType = itemSave.weaponType; item.stackable = itemSave.stackable; item.maxStack = itemSave.maxStack; item.reqLevel = itemSave.reqLevel; item.classRestriction = itemSave.classRestriction; item.reqStrength = itemSave.reqStrength; item.reqDexterity = itemSave.reqDexterity; item.reqIntellect = itemSave.reqIntellect; item.reqLevel = itemSave.reqLevel; item.minDamage = itemSave.minDamage; item.maxDamage = itemSave.maxDamage; return item; } } No change there at all from the original. The line it points to is Code (csharp): item.weaponType = itemSave.weaponType; I tried to add in a new type of item, but I didn't change the existing ones at all.
Wolf Dreamer: Check that WeaponSaver.cs in the Assets/Scrips/SaveSystem have weaponType variable, you showing Weapon.cs
Code (csharp): using UnityEngine; using System.Collections; [System.Serializable] public class WeaponSaver { public string name; //weapon name public string fullName; //full weapon name public string description; //weapon description public int price; //weapon price public string iconName = ""; //weapon icon resource name public bool stackable; //if item unique or can stack for potions, spell scrolls and like public int maxStack; //max items in stack public int reqLevel; //player min level to use item public int reqStrength; //player minimum attributes required to use/equip item public int reqDexterity; //0 - no requirement public int reqIntellect; // public CharacterClasses classRestriction; // public float minDamage; //weapon min-max damage public float maxDamage; // public string projectileName; //weapon projectile instaniate on fire public string modelName; //weapon model name public WeaponType WeaponType; //weapon type public static explicit operator WeaponSaver(Weapon item) { WeaponSaver itemSave = new WeaponSaver(); itemSave.name = item.name; itemSave.fullName = item.fullName; itemSave.description = item.description; itemSave.price = item.price; if (item.icon!=null) itemSave.iconName = item.icon.name; itemSave.stackable = item.stackable; itemSave.maxStack = item.maxStack; itemSave.reqLevel = item.reqLevel; itemSave.weaponType = item.weaponType; itemSave.minDamage = item.minDamage; itemSave.maxDamage = item.maxDamage; if (item.projectile != null) itemSave.projectileName = item.projectile.name; if (item.model != null) itemSave.modelName = item.model.name; itemSave.weaponType = item.weaponType; itemSave.classRestriction = item.classRestriction; itemSave.reqStrength = item.reqStrength; itemSave.reqDexterity = item.reqDexterity; itemSave.reqIntellect = item.reqIntellect; return itemSave; } } Didn't change that one either. Can you tell me the proper way to add in new elements? Because apparently whatever I'm doing is causing problems in unrelated scripts.
Here is you mistake, change Code (csharp): public WeaponType WeaponType; //weapon type in WeaponSaver.cs to Code (csharp): public WeaponType weaponType; //weapon type variable and enum type have the same name, upper and lower case matters.
Yeesh! How'd I miss that? I thought I checked it. Anyway... got other errors now. Instead of just armor, weapon, and potion, how do I create something else? I copied ArmorEditor.cs, Armor.cs object, and ArmorSaver.cs to Button1Editor.cs, Button1.cs, and Button1Saver.cs, and then edited those three new files, changing the name armor to Button1. Anywhere else I found it calling any of those armor files(in the same places it also called WeaponEditor, Potion.cs, etc) I copy and pasted what it listed for armor, and then changed the word armor to Button1. I'd like to have it where I can label things for the head, feet, legs, arms, torso, hands, etc, as well as have it where I can drag something from my skills/spells list to lock it to an area I create at the bottom for it. Be able to fire spells and attacks that way. Any suggestions? Code (csharp): using UnityEngine; using System.Collections; using System.Collections.Generic; public class InventoryControl { private List<InventoryItem> InventoryItems = new List<InventoryItem>(); public void LoadItems() { //Load weapons Hashtable toLoad = BinarySaver.Load(GlobalSettings.File_Weapons) as Hashtable; if (toLoad == null) { Debug.Log("No weapons file Found"); return; } ICollection itemCollection = toLoad.Values; foreach (WeaponSaver listWeapon in itemCollection) { Weapon loadWeapon = (Weapon)listWeapon; InventoryItems.Add(loadWeapon); } //Load armor toLoad.Clear(); toLoad = BinarySaver.Load(GlobalSettings.File_Armor) as Hashtable; if (toLoad == null) { Debug.Log("No armor file Found"); return; } itemCollection = toLoad.Values; foreach (ArmorSaver listArmor in itemCollection) { Armor loadArmor = (Armor)listArmor; InventoryItems.Add(loadArmor); } //Load potion toLoad.Clear(); toLoad = BinarySaver.Load(GlobalSettings.File_Potions) as Hashtable; if (toLoad == null) { Debug.Log("No potion file Found"); return; } itemCollection = toLoad.Values; foreach (PotionSaver listPotion in itemCollection) { Potion loadPotion = (Potion)listPotion; InventoryItems.Add(loadPotion); } //Load Button1 toLoad.Clear(); toLoad = BinarySaver.Load(GlobalSettings.File_Button1) as Hashtable; if (toLoad == null) { Debug.Log("No Button1 file Found"); return; } itemCollection = toLoad.Values; foreach (Button1Saver listButton1 in itemCollection) { Button1 loadButton1 = (Button1)listButton1; InventoryItems.Add(loadButton1); } } public InventoryItem FindItem(string itemName) { InventoryItem itemFind = InventoryItems.Find(delegate(InventoryItem item) { if (item.name == itemName) return true; return false; }); if (itemFind != null) { if (itemFind is Weapon) { Weapon retItem = new Weapon((Weapon)itemFind); return retItem; } if (itemFind is Armor) { Armor retItem = new Armor((Armor)itemFind); return retItem; } if (itemFind is Potion) { Potion retItem = new Potion((Potion)itemFind); return retItem; } if (itemFind is Button1) { Button1 retItem = new Button1((Button1)itemFind); return retItem; } } return itemFind; } }
Wolf Dreamer: for cast error, check all explicit conversions in button and buttonSaver, when copy-past code is most probably you miss something when replacing armor with button. For differnt slots is better create Slot class, something like: Code (csharp): public class Slot { public Rect slotRect; public InventoryItem slotItem; public SlotType slotType; } then create array in character class: Code (csharp): public InventoryItem[] inventory = new InventoryItem[10]; define constants to use instead numbers: Code (csharp): public const int head = 0; public const int leftArm = 1; public const int rightArm = 2; etc,etc... so you can access your slots like this: Code (csharp): item = inventory[head]; and use for..each statment for inventory when drawing interface and check for drag and drop. To check that items drops in their slot, you can create SlotType enum and add it to Slot class and InventoryItem class and simple check that item.slotType == slot.slotType Code (csharp): public enum SlotType { Head, Arm, Leg, Ring }
Wow .. good job with this code mate. I was looking for a tutorial on how to use enums but this is 500x better than anthing I found. Must feel good to inadvertantly be teaching so many others while your trying to learn, eh? hehehe
larrys: replace it with Code (csharp): private GUISkin defaultSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
private GUISkin defaultSkin =GUIUtility.GetBuiltinSkin(1); Assets/Scripts/Global/GameControl.cs(26,41): error CS0122: `UnityEngine.GUIUtility.GetBuiltinSkin(int)' is inaccessible due to its protection level why??
Sorry to necro this post, but I found this code and wanted to play with it a bit. However, it looks like some methods that are used in this code are not available in the latest version of unity. Unity throws an error at this line: private GUISkin defaultSkin = GUIUtility.GetBuiltinSkin(1); As I understand it GetBuiltinSkin() no longer exists. Is there a way to fix this without having to refactor all the code?