Unity - Creating a UI list at runtime programmatically - c#

I have a scene where I should be able to select a 3D model from a list of items, which will then be loaded into the next scene. I want to generate the list of items programmatically, based on what is already stored in the local database. But I have problems getting the list of items to be displayed in the scene ...
So I have a class for the items:
[System.Serializable] public class Item {
public string ObjectGuid;
public string Name;
public string Description;
public Sprite Icon;
public Button.ButtonClickedEvent SelectObject;
}
A script for my scene, that contains a list of items and a method that I call to load the objects from the database:
public class ObjectSelectionScript : MonoBehaviour {
public List<Item> itemList;
public Transform contentPanel;
void LoadObjects() {
StreamReader str = new StreamReader (Application.persistentDataPath + "/" + DataManager.LOCATION_XML_PATH);
string result = str.ReadToEnd ();
str.Close();
var jo = SimpleJSON.JSON.Parse (result);
if (jo ["Objects"] != null) {
for (int i = 0; i < jo["Objects"].Count; i++) {
if (!string.IsNullOrEmpty(jo["Objects"][i]["FileGuid"])) {
Item newObject = new Item();
newObject.ObjectGuid = jo["Objects"][i]["ObjectGuid"];
newObject.Name = jo["Objects"][i]["Name"];
newObject.Description = jo["Objects"][i]["Description"];
//newObject.SelectObject = new Button.ButtonClickedEvent();
itemList.Add(newObject);
}
}
}
}
A ModelObject class:
public class ModelObject : MonoBehaviour {
public Button button;
public Text Name;
public Text Description;
public Image Icon;
}
And a prefab ModelObjectPrefab that contains Name, Description, a Thumbnail, added to my assests folder.
The PopulateList() method is my problem. I can't get to instatiate the prefab and then create ModelObjects that I can throw in the contentPanel in the interface. This is my code:
void PopulateList()
{
try {
foreach (var item in itemList)
{
GameObject newModel = Instantiate(ModelObject) as GameObject;
ModelObject myModelObject = newModel.GetComponent<ModelObject>();
myModelObject.Name.text = item.Name;
myModelObject.Description.text = item.IDescription;
myModelObject.icon.sprite = item.Icon;
myModelObject.button.onClick = item.selectObject;
newModel.transform.SetParent(contentPanel);
}
} catch (System.Exception ex) {
}
}
I currently get null pointer on instantiate. I have tried Resources.Load, and variations of the Instatiate method, but I did not find a solution.
Can anyone give me a hint what is the problem? How should PopulateList() look like to get the items to the interface?

Note that the Instantiate() method should not be given a type (eg. ModelObject) as an argument. Rather, you should be passing it an object instance (in this case, your prefab GameObject "ModelObjectPrefab"). The documentation specifically indicates that it requires:
An existing object that you want to make a copy of
Otherwise, you're probably going to get unexpected behaviour like this. In your code, we need to pass a reference to "ModelObjectPrefab" as an argument of the Instantiate() call. I would suggest modifying your class containing PopulateList() to look like:
// New variable to hold prefab object
public GameObject modelObjectPrefab;
// ...
void PopulateList()
{
try {
foreach (var item in itemList)
{
// Reference prefab variable instead of class type
GameObject newModel = Instantiate(modelObjectPrefab) as GameObject;
ModelObject myModelObject = newModel.GetComponent<ModelObject>();
myModelObject.Name.text = item.Name;
myModelObject.Description.text = item.IDescription;
myModelObject.icon.sprite = item.Icon;
myModelObject.button.onClick = item.selectObject;
newModel.transform.SetParent(contentPanel);
}
} catch (System.Exception ex) {
}
}
Then, in the Editor window, drag your prefab "ModelObjectPrefab" from the assets panel onto the new Model Object Prefab field in the script properties, to form the connection between the script and the prefab.
Hope this helps! Let me know if you have any questions.

Note for readers. "GUI" is no longer available in Unity. Simply use the new UI system. Example http://www.folio3.com/blog/creating-dynamic-scrollable-lists-with-new-unity-canvas-ui/
The only thing that did work was to create the whole GUI programmatically.
I created a GUI.matrix and added everything inside it. It did not look very good, so I spent a lot of time trying to make it look better and to be responsive on mobile devices, but in the end this was the only solution that worked.
Just as a quick reference for who ever might have the same issue, the solution has this structure:
public void OnGUI ()
{
var savedMatrix = GUI.matrix;
scrollPosition = GUI.BeginScrollView (new Rect (10f, 10f, Screen.width - 10f, Screen.height - 10f), scrollPosition, new Rect (10f, 10f, Screen.width - 10f, (_files.Count + 2) * 200f));
if (Input.touchCount == 1) {
Vector2 touchDelta2 = Input.GetTouch (0).deltaPosition;
scrollPosition.y += touchDelta2.y;
}
var selected = GUILayout.SelectionGrid (gridInt, _files.ToArray (), 1, MyStyle);
if (selected >= 0) {
if (selected == 0) {
if (selectedItem != null )
if (selectedItem.Parent != null)
selectedItem = selectedItem.Parent;
else
selectedItem = null;
} else {
SelectFolder (selected);
}
RefreshViewModel ();
}
GUI.EndScrollView ();
GUI.matrix = savedMatrix;
}

Related

Stop animation from array in Unity and C#

In my Unity project I have several levels and several steps. Each step and level have their own animations. With a button click I want to stop all animations, so I tried it the following way. For my project I have two scripts:
Start_Script.cs
//Steps for Level 01:
public static GameObject Step001, Step011, Step012;
//Each animation will play in Level 01:
public static Animation AnimationStep001, AnimationStep011, AnimationStep012;
int levelCounter;
void Awake()
{
levelCounter = 1;
Step001 = GameObject.Find("Step001");
Step011 = GameObject.Find("Step011");
Step012 = GameObject.Find("Step012");
AnimationStep001 = Step001.GetComponent<Animation>();
AnimationStep011 = Step011.GetComponent<Animation>();
AnimationStep012 = Step012.GetComponent<Animation>();
}
//Method is assigned to "Next Level" Button.
public void nextLevel()
{
switch(levelCounter)
{
case 1:
for(int i = 0; i == Array_Lists.animStrings.Length; i++)
{
Array_Lists.animAnimation[i].Stop(Array_Lists.animStrings[i]);
}
break;
}
levelCounter += 1;
}
Array_Lists.cs
// My Array for the animations name:
public static string[] animStrings = {"animStep1", "animStep1.1", "animStep1.2"};
// My Array for the animation objects:
public static Animation[] animAnimation = {
Start_Script.AnimationStep001,
Start_Script.AnimationStep011,
Start_Script.AnimationStep012
};
But unfortunately I always get a NullReferenceException: Object reference not set to an instance of an object for Array_Lists.animAnimation[i].Stop(Array_Lists.animStrings[i]);
In summary, I need to stop all animations when I hit the "Next Level" Button.
Edit
After my first NullReferenceException: I added a the following to my public void nextLevel() method:
public void nextLevel()
{
switch(levelCounter)
{
case 1:
for(int i = 0; i == Array_Lists.animStrings.Length; i++)
{
Debug.Log("Array Animations = " + Array_Lists.animAnimation[i];
Debug.Log("Array anim Name = " + Array_Lists.animStrings[i];
Array_Lists.animAnimation[i].Stop(Array_Lists.animStrings[i]);
}
break;
}
levelCounter += 1;
}
This was my output:
Array Animations = null
Array anim Name = animStep1
Array Animations = null
Array anim Name = animStep1.1
Array Animations = null
Array anim Name = animStep1.2
Your arrays are static, so they are created before Awake.
Your animAnimation is filled with nulls, and it doesn't change when you put components to Start_Script.AnimationStep001 and others. You need to fill animAnimation with components after Awake. And your scripts are interlinked, this is not very good. Consider creating and populating animAnimation from Start_Script.
UPD: add this at the end of your Start_Script.Awake:
Array_Lists.animAnimation = new [] {
AnimationStep001,
AnimationStep011,
AnimationStep012
};
i == Array_Lists.animStrings.Length
and not
i < Array_Lists.animStrings.Length
I suspect you never enter the for cycle.

How can I successfully store and load data from a game character?

I was wrote a script that combines whale characters to create a new character. However, it is difficult to save and load data from a list of names, levels and locations of the newly created characters.
Whale data does not seem to be stored and loaded when the game is played.
I'd really thanks it if you could tell me where's wrong.
using system;
using System.Collections;
using System.Collections.General;
using system.IO;
using LitJson;
using UnityEngine;
using Random = UnityEngine.Random;
[System.Serializable]
Public class WhaleData
{
Public in dataLevel{get; set;// whale level
public Vector3 dataMousePosition{get; set;//the whale location
public string dataName{get; set;} //whale name
}
Public class Whale: MonoBehaviour
{
public int level;
Private pool isDrag;
Public GameObject NextPrepab;
Private Vector3 mousePosition;
Public WhaleData whaleData;
Private List[WhaleData] WhaleDatas = new List[WhaleData]();
private pool IsSave; //bool value to check for stored data
void start()
{
IsSave = PlayerPrefs.HasKey ("0_name"); //saved_level Check if a key value named //saved_level exists
//initialize all data values if no save data exists
If (!IsSave)
{
Debug.Log ("No data stored).");
PlayerPrefs.DeleteAll();
}
//recall value if save data exists
else
{
Debug.Log ("Stored Data").");
LoadData();
}
}
Private void Update ()
{
SaveData();
LoadData();
}
private void onMouseDown()
{
isDrag = true;
}
private void OnMouseDrag()
{
if (isDrag)
{
mousePosition.x = Input.mousePosition.x;
mousePosition.y = input.mousePositionY;
mousePosition.z = 10;
//change the mouse coordinates to the screen to world and set the position of this object
gameObject.transform.position = Camera.main.ScreenToWorldPoint(mousePosition);
// store the location of the whale in the whalData.dataMousePosition= gameObject.transform.position; //
}
}
private void onMouseUp()
{
//OverlapSphere : Return from the specified location to the collider array in contact within range
var colliders = Physics.OverlapSphere (transform.position, 0.1f);
foreach (var col in colliders) // arrangement of surrounding contacts
{
If (name!= col. gameObject.name) //if the name is different, go
{
If (level==col.gameObject.GetComponent[Whale>().level) //If the level of contact with the level stored in me is the same,
{
whatData.dataLevel = col.gameObject.GetComponent[Whale>().level; // store the level of the whale in the whalData class
var newWhale = Instantiate(NextPrepab, transform.position, quaternion.identity);
newWhale.name = Random.Range (0f, 200f).ToString(); //name is random.
dateData.dataName = name;// store the name of the whale in the whalData class
SaveData();
print(col.gameObject.name);
print(gameObject.name);
whatDatas.Add(whaleData);
Destroy (col.gameObject);
Destroy (gameObject);
// delete if there is any whale information with the same name as Whale's name that will disappear from the dateDatas list (use a simple calculation expression of the unknown method)
if (whaleDatas.Find(whaleData=>whaleData.dataName=colgameObject.GetComponent[Whale>(.name).dataName==colgameObject.GetComponent[Whale>(.name)
{
whaleDatasRemove (col.gameObject.GetComponent[Whale>(whaleData));
Destroy (col.gameObject);
Destroy (gameObject);
}
else
{
break;
}
break;
}
}
}
isDrag = false;
}
Public static void setVector3 (string key, Vector3 value)
{
PlayerPrefs.SetFloat (key + "X", value.x);
PlayerPrefs.SetFloat (key + "Y", value.y);
PlayerPrefs.SetFloat (key + "Z", value.z);
}
Public static Vector3 GetVector3 (string key)
{
Vector3 v3 = Vector3.zero;
v3.x = PlayerPrefs.GetFloat (key + "X");
v3.y = PlayerPrefs.GetFloat (key + "Y");
v3.z = PlayerPrefs.GetFloat (key + "Z");
return v3;
}
private void saveData()
{
for (int i=0; i <whaleDatas.Count; i++)
{
PlayerPrefs.SetString(i+"_name",whaleDatas[i‐dataName));
PlayerPrefs.SetInt(i+"_level",whaleDatas[i‐dataLevel));
Vector3 value = whatDatas[i].dataMousePosition;
string key = i + "_position";
SetVector3 (key,value);
PlayerPrefs.Save();
Debug.Log ("Saved");
}
}
private void LoadData()
{
for(int i=0; i <whaleDatas.Count; i++)
{
whaleDatas[i].dataName = PlayerPrefs.GetString(i+"_name");
whaleDatas[i].dataLevel = PlayerPrefs.GetInt(i+"_level");
string key = i + "_position";
whaleDatas[i].dataMousePosition = GetVector3(key);
}
}
}
Your code is actually broken in term of design.
First PlayerPref doesnt support boolean save by default.
IsSave = PlayerPrefs.HasKey //this will always return false.
thats why everytime you start the game all your saved data is deleted.
You can try this work around to save and load a boolean type How to save bool to PlayerPrefs Unity
Now, How on earth you decided to save and load data at update function?! , this is terrible and has huge performance cost if your data is huge. you just cant do it this way.
Instead, try to make events that store the data and load it instead, like once player enter a trigger box , the data is saved, and ofcourse you dont need to reload it if its saved , bec why you would have to load the data after its being saved?!!
Once again, dont read and write at Update Function like this all the time, this is bad design .

make changes to material assets in unity3d

I am overriding some Material Assets using an Editor Script, and materials override nicely. I can see new material properties get applied and when I click on individual materials I can see new textures applied etc. However when I hit play, my materials reset to pre-edit state, Same thing happens when I hit CTR+S. My materials all reset back to what they were.
How can I make the changes get saved to database and persist when I hit play?
using UnityEngine;
using UnityEditor;
using Newtonsoft.Json;
using Unify.Utilities;
using System.Collections.Generic;
using System;
using System.IO;
public class ProcessMaterials : MonoBehaviour
{
[MenuItem("Unify/ProcessMaterials")]
static void UnifyProcessMaterials()
{
ImportTextures();
ApplyMaterials();
}
private static void ImportTextures()
{
// check if folder exists and create one if not
if (!AssetDatabase.IsValidFolder("Assets/Resources"))
{
AssetDatabase.CreateFolder("Assets", "Resources");
}
// load settings file
TextAsset ta = Resources.Load("UnifySettings") as TextAsset;
string json = ta.text;
List<List<UnifyObject>> unifyObj = JsonConvert.DeserializeObject<List<List<UnifyObject>>>(json);
List<UnifyObject> allMats = unifyObj[3];
// copy textures over to unity folders
HashSet<string> uniqueTextures = new HashSet<string>();
foreach (UnifyObject obj in allMats)
{
if (obj.DiffuseTexture != null && uniqueTextures.Add(obj.DiffuseTexture))
{
CopyImageAsset(obj.DiffuseTexture);
}
if (obj.BumpTexture != null && uniqueTextures.Add(obj.BumpTexture))
{
CopyImageAsset(obj.BumpTexture);
}
if (obj.TransparencyTexture != null && uniqueTextures.Add(obj.TransparencyTexture))
{
CopyImageAsset(obj.TransparencyTexture);
}
if (obj.EnvironmentTexture != null && uniqueTextures.Add(obj.EnvironmentTexture))
{
CopyImageAsset(obj.EnvironmentTexture);
}
}
}
private static void CopyImageAsset(string sourceFilePath)
{
string fileName = "Resources\\" + Path.GetFileName(sourceFilePath);
string destFile = Path.Combine(Application.dataPath, fileName);
try
{
File.Copy(sourceFilePath, destFile, true);
}
catch (Exception) { }
}
private static void ApplyMaterials()
{
TextAsset ta = Resources.Load("UnifySettings") as TextAsset;
string json = ta.text;
List<List<UnifyObject>> unifyObj = JsonConvert.DeserializeObject<List<List<UnifyObject>>>(json);
GameObject cube;
cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Renderer cubeRenderer = cube.GetComponent<Renderer>();
List<UnifyObject> allMaterials = unifyObj[3];
foreach (UnifyObject obj in allMaterials)
{
// skip layers with no materials assigned/default
if (obj.Guid != Guid.Empty.ToString())
{
// obj replaces all dashes in names with underscores hence material assets will have different names than in Rhino
// if layers had dashes in their names
string objUniqueName = obj.UniqueName.Replace("-", "_");
Material m = (Material)AssetDatabase.LoadAssetAtPath("Assets/Resources/Model/Materials/" + objUniqueName + "Mat.mat", typeof(UnityEngine.Object));
if (m != null)
{
OverrideMaterial(m, obj, cubeRenderer);
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(m));
}
}
}
DestroyImmediate(cube);
}
private static Material OverrideMaterial(Material m, UnifyObject obj, Renderer renderer)
{
renderer.material = m;
// set main color
// set transparency
if (obj.Transparency != "0")
{
Color newColor = Utilities.ConvertToUnityColor(obj.Diffuse, obj.Transparency);
renderer.sharedMaterial.SetFloat("_Mode", 3);
renderer.sharedMaterial.SetColor("_Color", newColor);
}
else
{
Color newColor = Utilities.ConvertToUnityColor(obj.Diffuse);
renderer.sharedMaterial.SetColor("_Color", newColor);
}
// set main texture
if (obj.DiffuseTexture != null)
{
renderer.sharedMaterial.mainTexture = Utilities.Texture2dFromPath(obj.DiffuseTexture);
}
// set bump map
if (obj.BumpTexture != null)
{
Texture2D bumpTexture = Utilities.Texture2dFromPath(obj.BumpTexture);
float strength = Convert.ToSingle("1.0");
Texture2D normalBump = Utilities.CreateNormalMap(bumpTexture, strength);
renderer.sharedMaterial.SetTexture("_BumpMap", normalBump);
// need to get that value from Rhino somehow
renderer.sharedMaterial.SetFloat("_BumpScale", 0.3f);
}
// set metallic
renderer.sharedMaterial.SetFloat("_Metallic", Utilities.ConvertRange(0, 255, 0, 1, Convert.ToSingle(obj.Metallic)));
// set emission color
Color emissionColor = Utilities.ConvertToUnityColor(obj.EmissionColor);
renderer.sharedMaterial.SetColor("_EmissionColor", emissionColor);
return renderer.sharedMaterial;
}
}
After you overwrite the materials, call the following functions
UnityEditor.EditorUtility.SetDirty(AssetName);
UnityEditor.AssetDatabase.SaveAssets();
UnityEditor.AssetDatabase.Refresh();
If the method above did not work, another method that might work is to create a simple cube, assign the loaded material to the cube then modify the Renderer.sharedMaterial of the cube instead of Renderer.material. Usually, modifying sharedMaterial changes the original material permanently but I don't know if applies to loaded materials from AssetDatabase. This should be done inside your OverrideMaterial function. The GameObject.CreatePrimitive(PrimitiveType.Cube); function should only be called once.
GameObject cube;
cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Renderer cubeRenderer = cube.GetComponent<Renderer>();
//Change the cube material to the material that is loaded from the disk
cubeRenderer.material = m;
//Now modify the shared array of the cube
cubeRenderer.sharedMaterial.SetFloat("_Mode", 3);
cubeRenderer.sharedMaterial.SetColor("_Color", newColor);
//cubeRenderer.sharedMaterial.
//cubeRenderer.sharedMaterial.

How can i keep track of which randomly selected model was generated so it won't repeat again?

This code generated a random game object but because there are only 9 game objects sometimes the randomly generated object keeps re-generating more than one time. How can i restrict that, and have one game object generated only once ?
public GameObject[] models;
public static GameObject currentPoint;
int index;
public static string randomName;
public AudioSource FindTheNumber;
public void PlayNumbers()
{
models = GameObject.FindGameObjectsWithTag ("numbers");
index = Random.Range (0,models.Length);
currentPoint = models [index];
randomName = currentPoint.name;
print ("Trackable " + randomName);
FindTheNumber.Play ();
currentPoint.GetComponent<AudioSource> ().PlayDelayed(2);
}
If I've understood correctly, how about keeping track of the selected game objects in a separate list.
public static List<GameObject> models;
public static List<GameObject> selectedModels = new List<GameObject>();
public static GameObject currentPoint;
int index;
public static string randomName;
public AudioSource FindTheNumber;
public static Random random = new Random();
public void PlayNumbers()
{
models = GameObject.FindGameObjectsWithTag("numbers").Except(selectedModels).ToList();
if ((models == null) || (!models.Any()))
{
Console.WriteLine("No new game objects");
}
else
{
index = random.Next(models.Count);
currentPoint = models[index];
randomName = currentPoint.name;
print ("Trackable " + randomName);
FindTheNumber.Play ();
currentPoint.GetComponent<AudioSource> ().PlayDelayed(2);
selectedModels.Add(currentPoint);
}
}
So your problem is that when you call currentPoint and create a sound for example you encounter the problem that the gameObject might get called more often creating the "same" object ?
If yes, you could save the last gameObject that was created and check if it is the same as the new one?
That way you always have a different gameObject every time ;). I don't know what's the delta between your calls and how long they last, but checking the gameObject should do the trick.
You could create a variable that stores information if the object is doing it's job.
Additionally you could check verify that the values differ from the gameObjects that are currently active.
If i understood your problem correct your code should look something like this.
public GameObject[] models;
public static GameObject currentPoint;
int index;
public static string randomName;
public AudioSource FindTheNumber;
public void PlayNumbers()
{
do {
models = GameObject.FindGameObjectsWithTag ("numbers");
index = Random.Range (0,models.Length);
currentPoint = models [index];
} while(currentPoint.getComponent<AudioSource>().isPlaying);
randomName = currentPoint.name;
print ("Trackable " + randomName);
FindTheNumber.Play ();
currentPoint.GetComponent<AudioSource> ().PlayDelayed(2);
}
Right now the code will loop atleast one time. If the object you are trying to use is already playing the code will loop through the array again.
Once it finds a object that isn't playing the code will proceed and do what it should do.
If you get any errors or if my solution is incorrect just post again and tell us what you want (in detail)
Have a nice day ;)
PS: The only thing that annoys me is it might double check some values due to the Random.Range...
One approach to the recommendation Vancold.at made
public void PlayNumbers()
{
var rnd = new Random();
models = GameObject.FindGameObjectsWithTag("numbers");
while (true)
{
index = rnd.Next(0, models.Length - 1);
var newPoint = models[index];
//assumes currentPoint is initially null the first time this method is called
if(currentPoint == null || newPoint.name != currentPoint.name)
{
currentPoint = newPoint;
break;
}
}
randomName = currentPoint.name;
print("Trackable " + randomName);
FindTheNumber.Play();
currentPoint.GetComponent<AudioSource>().PlayDelayed(2);
}
Keep in mind that this only assures the same GameObject instance isn't used two times in a row. It will not prevent same object from being used more than once. For example, if you have 9 GameObject instances with names "1", "2", "3", "4", "5", "6", "7", "8", "9", the code above allows the following output:
Trackable 5
Trackable 7
Trackable 5
Trackable 4
Trackable 7
Etc. If you want to cycle through the possible values once before repeating, then a different approach is needed.
I found the solution, I will post the code I wrote so it may serve to someone in the future:
public GameObject[] models;
public static GameObject currentPoint;
int index;
public static string randomName;
public AudioSource FindTheNumber;
int i =9;
public void PlayNumbers()
{
models = GameObject.FindGameObjectsWithTag ("numbers");
if (i >= 0)
{
index = i;
currentPoint = models [index];
randomName = currentPoint.name;
print ("Trackable " + randomName);
FindTheNumber.Play ();
currentPoint.GetComponent<AudioSource> ().PlayDelayed (2);
i--;
}
else
{
i=9;
index = i;
currentPoint = models [index];
randomName = currentPoint.name;
print ("Trackable " + randomName);
FindTheNumber.Play ();
currentPoint.GetComponent<AudioSource> ().PlayDelayed (2);
i--;
}
}

Array error in C# for Unity

Hey everyone i am trying to make an inventory list in C# for unity but i run into an array error when i pickup my item, I cant figure out why i wondered if someone could help. I have searched everywhere for some tips but not come across any the error is :-
ArgumentException: Destination array was not long enough. Check destIndex and length, and the array's lower bounds
As an edit iv attached the full code
code attached below :-
using UnityEngine;
using System.Collections;
[AddComponentMenu ("Inventory/Inventory")]
public class Inventory : MonoBehaviour {
//This is the central piece of the Inventory System.
public Transform[] Contents; //The content of the Inventory
public int MaxContent = 12; //The maximum number of items the Player can carry.
bool DebugMode = true; //If this is turned on the Inventory script will output the base of what it's doing to the Console window.
private InventoryDisplay playersInvDisplay; //Keep track of the InventoryDisplay script.
public Transform itemHolderObject; //The object the unactive items are going to be parented to. In most cases this is going to be the Inventory object itself.
//Handle components and assign the itemHolderObject.
void Awake (){
itemHolderObject = gameObject.transform;
playersInvDisplay = GetComponent<InventoryDisplay>();
if (playersInvDisplay == null)
{
Debug.LogError("No Inventory Display script was found on " + transform.name + " but an Inventory script was.");
Debug.LogError("Unless a Inventory Display script is added the Inventory won't show. Add it to the same gameobject as the Inventory for maximum performance");
}
}
//Add an item to the inventory.
public void AddItem ( Transform Item ){
ArrayList newContents = new ArrayList();
//FIXME_VAR_TYPE newContents= new Array(Contents);
newContents.Add(Item);
//Contents=newContents.ToBuiltin(Transform); //Array to unity builtin array
newContents.CopyTo(Contents); //Array to unity builtin array
System.Array.Resize(ref Contents, 1);
if (DebugMode)
{
Debug.Log(Item.name+" has been added to inventroy");
}
//Tell the InventoryDisplay to update the list.
if (playersInvDisplay != null)
{
playersInvDisplay.UpdateInventoryList();
}
}
//Removed an item from the inventory (IT DOESN'T DROP IT).
public void RemoveItem ( Transform Item ){
ArrayList newContents = new ArrayList();
//FIXME_VAR_TYPE newContents=new Array(Contents); //!!!!//
int index = 0;
bool shouldend = false;
foreach(Transform i in newContents) //Loop through the Items in the Inventory:
{
if(i == Item) //When a match is found, remove the Item.
{
newContents.RemoveAt(index);
shouldend=true;
//No need to continue running through the loop since we found our item.
}
index++;
if(shouldend) //Exit the loop
{
//Contents=newContents.ToBuiltin(Transform); //!!!!//
Contents=newContents.ToArray(typeof (Transform)) as Transform[];
if (DebugMode)
{
Debug.Log(Item.name+" has been removed from inventroy");
}
if (playersInvDisplay != null)
{
playersInvDisplay.UpdateInventoryList();
}
return;
}
}
}
//Dropping an Item from the Inventory
public void DropItem (Item item){
gameObject.SendMessage ("PlayDropItemSound", SendMessageOptions.DontRequireReceiver); //Play sound
bool makeDuplicate = false;
if (item.stack == 1) //Drop item
{
RemoveItem(item.transform);
}
else //Drop from stack
{
item.stack -= 1;
makeDuplicate = true;
}
item.DropMeFromThePlayer(makeDuplicate); //Calling the drop function + telling it if the object is stacked or not.
if (DebugMode)
{
Debug.Log(item.name + " has been dropped");
}
}
//This will tell you everything that is in the inventory.
void DebugInfo (){
Debug.Log("Inventory Debug - Contents");
int items=0;
foreach(Transform i in Contents){
items++;
Debug.Log(i.name);
}
Debug.Log("Inventory contains "+items+" Item(s)");
}
//Drawing an 'S' in the scene view on top of the object the Inventory is attached to stay organized.
void OnDrawGizmos (){
Gizmos.DrawIcon (new Vector3(transform.position.x, transform.position.y + 2.3f, transform.position.z), "InventoryGizmo.png", true);
}
}
Hope someone could help thank you in advance :)
Arrays have a fixed size when they are allocated only complex types that mimic array interaction have auto grow functionality.
Check the capacity of the target array you are copying your original array contents too.
I am not sure what ToBuiltIn method does but it must be creating a new array as a copy of the origin (your data might already be in the array by the way, check it by debugging it) with the capacity set to the total number of items in the array which could be 1 or 0.
Check your variables and debug the code to see the lengths and capacities of you arrays.
UPDATE (From the updated code)
Your Contents array is not initialized when calling the Add Item method.
To make it clear I have rewritten the code you pasted I dont have all the classes you are using so I have not tested or checks it it builds however it will give you an idea of what you can do. Your array management can be simplified if you use generic list.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[AddComponentMenu ("Inventory/Inventory")]
public class Inventory : MonoBehaviour {
//This is the central piece of the Inventory System.
public List<Transform> Contents; //The content of the Inventory
public int MaxContent = 12; //The maximum number of items the Player can carry.
bool DebugMode = true; //If this is turned on the Inventory script will output the base of what it's doing to the Console window.
private InventoryDisplay playersInvDisplay; //Keep track of the InventoryDisplay script.
public Transform itemHolderObject; //The object the unactive items are going to be parented to. In most cases this is going to be the Inventory object itself.
public Inventory()
{
this.Contents = new List<Transform> (MaxContent);
}
//Handle components and assign the itemHolderObject.
void Awake (){
itemHolderObject = gameObject.transform;
playersInvDisplay = GetComponent<InventoryDisplay>();
if (playersInvDisplay == null)
{
Debug.LogError("No Inventory Display script was found on " + transform.name + " but an Inventory script was.");
Debug.LogError("Unless a Inventory Display script is added the Inventory won't show. Add it to the same gameobject as the Inventory for maximum performance");
}
}
//Add an item to the inventory.
public void AddItem ( Transform Item ){
if (this.Contents.Count < this.MaxContent) {
Contents.Add (Item);
if (DebugMode) {
Debug.Log (Item.name + " has been added to inventroy");
}
//Tell the InventoryDisplay to update the list.
if (playersInvDisplay != null) {
playersInvDisplay.UpdateInventoryList ();
}
} else {
// show message that inventory is full
}
}
//Removed an item from the inventory (IT DOESN'T DROP IT).
public void RemoveItem ( Transform Item ){
if (this.Contents.Remove (Item)) {
if (DebugMode) {
Debug.Log (Item.name + " has been removed from inventroy");
}
if (playersInvDisplay != null) {
playersInvDisplay.UpdateInventoryList ();
}
return;
} else {
// Item is not in inventory
}
}
//Dropping an Item from the Inventory
public void DropItem (Item item){
gameObject.SendMessage ("PlayDropItemSound", SendMessageOptions.DontRequireReceiver); //Play sound
bool makeDuplicate = false;
if (item.stack == 1) //Drop item
{
RemoveItem(item.transform);
}
else //Drop from stack
{
item.stack -= 1;
makeDuplicate = true;
}
item.DropMeFromThePlayer(makeDuplicate); //Calling the drop function + telling it if the object is stacked or not.
if (DebugMode)
{
Debug.Log(item.name + " has been dropped");
}
}
//This will tell you everything that is in the inventory.
void DebugInfo (){
Debug.Log("Inventory Debug - Contents");
int items=0;
foreach(Transform i in Contents){
items++;
Debug.Log(i.name);
}
Debug.Log("Inventory contains "+items+" Item(s)");
}
//Drawing an 'S' in the scene view on top of the object the Inventory is attached to stay organized.
void OnDrawGizmos (){
Gizmos.DrawIcon (new Vector3(transform.position.x, transform.position.y + 2.3f, transform.position.z), "InventoryGizmo.png", true);
}
}

Categories