Is there any way to get an array containing un-instantiated prefabs - c#

I have a folder, Items, that contains prefabs with the class attached, what I want to do is make an array full of these prefabs in order to compare their ID numbers, a string inside the item class, with the ID numbers from a database to put these prefabs into an inventory array, I hope this is enough detail.

This is what you probably need.
public List<GameObject> prefabsWithClassOnThem;
public GameObject CheckTheID(string ID)
{
foreach (GameObject go in prefabsWithClassOnThem)
{
if(go.GetComponent<ItemClass>().getID() == ID){
return go;
}
}
return null;
}
prefabsWithClassOnThem - the name speaks for itself. It is the list of prefabs you will have to put manually into the list. Then you add your script to a script manager (or any object you feel like) this is how it will look like:
Click on the arrow, write in wanted size and put all the prefabs manually.
CheckTheID - method, which will check for a prefab with the ID you give for this method.
ItemClass - the class, you were mentioning in your question. The class which holds the ID of prefab, and has method GetID().
You will want to call this method in your addToInventory method or something like this.
If you want to check if object was instantiated before, just create additional boolean parameter in ItemClass which will hold true if it was instantiated (you will have to change it for the first time you instantiate one of the prefabs).
Every time you want to create unique item, just check the boolean and thats pretty much it.
Sorry for messy language.

Related

Unity3D. Instantiating and displaying enum objects using custom inspector

My script extracts all child objects from a parent object. I need to assign materials individually to child objects or material to all objects via parent object.
Currently when I change a child material, all other child object materials get changed aswell.
How it looks in Unity when I change a child material
Here is my code
if (includeChildObj == true)
{
EditorGUILayout.Foldout(includeChildObj, "List of Child Objects", includeChildObj);
for (int i = 0; i < Geometry.getChildNames().Count; i++) // loop through all child objects
{
GUILayout.BeginHorizontal();
GUILayout.Label(Geometry.getChildNames()[i]); // display object name
EditorGUILayout.PropertyField(serializedObject.FindProperty("SelectMaterial")); // find an enum which allows the dropdown list
GUILayout.EndHorizontal();
serializedObject.ApplyModifiedProperties();
}
}
How is see it is that inside the FOR loop, for every Geometry object, I need to instantiate a new object of the corresponding enum. I am not sure how to do this as I am using CustomEditor not Monobehaviour.
Thank you!
It appears that you are referencing the same serialized object in each iteration of your for loop. So there's only one value being read and assigned to in each iteration, which explains the behaviour you're seeing.
It's not clear to me what your requirements are, but perhaps you need multiple instances of that serialized object, one per Geometry child?

Read the modified object

How to get ObjectId of the recently modified object?
I add some polyline to the drawing. Each one is the same. When one modifies the other, they must also be adjusted to the changes. I need to exclude from the list of objects that need to be updated, the object I just modified. I can not find the answer.
Example:
I have some polyline. When creating them, an event handler is added (ObjectId of each object is added to NOD). At the moment of modifying one of them, the function assigned to the modification will loop through all objects stored in NOD. When the length is changed other objects must also do so. Initially I want to do that the rest will be removed and replaced with a modified copy of the object.
Here I need to access the last modified object to be able to skip it while modifying other polyline. At the moment, the program ends and I think this is a problem because I'm trying to convert the polylines to the same one.
If I understand your problem correctly, what you need is a dynamic exclusion list. A Hashset<ObjectID> instance will serve you well, as ObjectID is a struct (i.e. a value type). See this tutorial:
HashSet is an unordered collection that contains unique elements. We can apply various operations on a HashSet like, Add, Remove, Contains etc.
Once a polyline is modified by user, I am assuming an event triggers your process to go ahead and modify all other polylines in your NOD. To utilize your exclusion list, add a private field to the class that contains your Command Methods:
private static HashSet<ObjectId> exclusionList = new HashSet<ObjectId>();
Make sure to add the ObjectID of the polyline that the user modified to the exclusionList right away:
exclusionList.Add(modifiedObjectID);
The Add() method returns true if the list does not contain the objectID you tried to add, and adds it to the set. It will return false if the set already contains the ObjectID. This is the key to solving your issue. You add each entity to the exclusion set so that each one is only modified once per cycle. Also, since you already added your original polyline's ObjectID to the set, it will never be modified in the loop. After the loop ends, clear the list and wait for the next time the user modifies an entity.
In your loop that cycles through your MOD, do the following:
public void ProcessEntities()
{
foreach(DBDictionaryEntry obj in MyNOD)
{
//Add each objectID to your exclusion List
//if it's already there, everything inside the if statement is skipped!
if(exclusionList.Add(obj.Value))
{
//do your object modification here
}
}
//All entities have been processed now
//clear the list and wait for the next event
exclusionList.Clear();
}

Retrieve all Animator States and set them manually in code

I've stumbled upon something I never imagined to be a problem but... it is.
I am writing a visualiazion/simulation toolkit using unity 5.5.
The used model is already imported and animation clips are set via the editor. This is what my animation state controller lookes like:
I want to display the AnimationStates in a DropdownMenu. Whenever a user selects an entry, the current animation state should be set to the selected one.
e.G. User selected Autoplay: State set to clip Autoplay, after that to SimpleRotate, then to ComplexRotate and again to Autoplay (just a simple loop).
But if the user selects Idle, ComplexRotate 0 or SimpleRotate 0, the animation should play once and then stay at the end of the clip. (Just like I did it in the animation controller.
Heres a rather pseudo-version of what i want:
//Retrieve all states to populate the dropdown:
List<AnimationState> states = myAnimationController.GetAllStates();
foreach(AnimationState state in states)
Dropdown.add(state.name)
//callback
OnDropdownIndexChanged(int item)
{
string stateName = ..... //removed for simplicity
myAnimationController.setState(stateName)
}
On top of that it would be nice if I could check the transitions FROM and TO the states. (Autoplay is the only state that should be shown in the dropdown for the loop)
Can this be realized with a custom Animation Controller? Or did I miss something?
I see two options here. A rather simple one and a bit more complex, but robust, one.
Option One
You use the RuntimeAnimatorController. This will get you all the AnimationClips in the given Animator at runtime. You can play a State in an AnimatorController at any given time by using Animator.Play(StateNameOrHash). Unfortunately, for this option, this will only play a State by its Name or NameHash (as statet in the docs). Because we only have the AnimationClips and not the States from the Controller this option would involve to rename the States in the controller to the exact name of the Animation Clip. Then you could play a State like this:
myAnimator.Play(myAnimationClip.name);
This is a fast option to get you started but not a good option in the long run because it has essentially two drawbacks:
You could only use a flat hierarchy in your AnimatorController. No SubStateMachines/Blendtrees because they are not represented with an AnimationClip. Therefore you would not be able to retrief these States through the RuntimeAnimatorController because it only returns the AnimationClips
The AnimatorController could be hard to understand because the states are named like the AnimationClips. I dont know your situation here but it would be better to use descriptive names then the names from the AnimationClips for better understanding.
Option Two
This option is more complex. I will give you a quick overview of the Workflow and then explain the parts in more detail or give references to get you started!
[Editor Script] Generate a script with all the States from your AnimatorController. (Probably the "hardest" part)
Use the generated Script to fill your dropdown with the States
Play your state :)
Part 1:
Use an Editor Script to extract all the States from your AnimatorController. This link should give you an hint on how to achieve this. Short (not tested) example code:
private void writeAllStates()
{
AnimatorControllerLayer[] allLayer = controller.layers;
List<string> stateNames = new List<string>();
for (int i = 0; i < allLayer.Length; i++)
{
ChildAnimatorState[] states = allLayer[i].stateMachine.states;
for (int j = 0; j < states.Length; j++)
{
// you could do additional filtering here. like "would i like to add this state to my list because it has no exit transition" etc
if (shouldBeInserted(states[i]))
{
// add state to list
stateNames.Add(states[j].state.name);
}
}
}
// now generate a textfile to write all the states
FileGenerator.createFileForController(controller, stateNames);
}
Keep in mind: the used AnimatorController is within the UnityEditor.Animations namespace. This can be a public field in an EditorScript to easily give you access.
Part 2: Your scriptfile should create a class like this one:
public class AnimatorControllerStates
{
public List<string> states = new List<string>
{
"Idle",
"ComplexRotate 0",
"Autoplay",
...
}
}
Name the class somehow related to your AnimatorController you created it for. And make this class a field in your runtime script. Then you could get access to all your states like this:
myAnimatorControllerStates.states
This array can fill your dropdown. You would save the index of the dropdown and then can play the state by the index.
Part 3: Now it would be realy easy to just play your state in your script:
string state = myAnimatorControllerStates.states[mySelectedIndex];
myAnimatorController.Play(mySelectedIndex);
I dont know the scope of your project but to have a maintainable application I would prefer option two at any time.

GetComponent in Unity (C#)

So i try to access the script component in another GameObject, but to do it faster i have a Component variable.
Component map = GameObject.FindWithTag("Map").GetComponent<Map>();
Now when i want to reference a variable inside a map component
map.selected = true;
it gives me an error that
UnityEngine.Component does not contain a definition for selected".
Selected is a public bool in Map script, attached to the Map GameObject.
When i go with the long way
GameObject.FindWithTag("Map").GetComponent<Map>().selected = true;
everything works, but i'm looking for a way to shorten my code (this script uses variables from Map in many instances, so i want to write it the shorter way).
This happens because you are instanciating your variable map as a Component, so the compiler will be looking for the selected attribute in the Component class.
Try something like this:
MapScript map = GameObject.FindWithTag("Map").GetComponent<MapScript>();
(Replace MapScript with the actual name of that script.)
I'm not entirely sure if this exact syntax will work, and I have no way of testing this at the moment, so please try and see if it works. If it doesn't, I will look into that as soon as I'm home.

Removing element from array: convert to list, remove element, convert back to array?

In my program I have a class called Album. I have an Array my Album objects go into on creation. I need to let the user change the albumName of an Album later if they want to. I also need to let the user delete Albums from the Array if they want to. From what I understand, you can't just delete objects from Arrays, so it would be good if they were stored in a List, not an Array.
From what I understand of Lists (I haven't used them before this), I can't put the Album objects into a List instead of an Array on creation if I want to change a variable in the Album object later. I also can't just delete and replace the Album object with a new one in the List if I want to change the albumName, since the Album object will be storing an Array (myMusics) that would be lost if I did that.
So my question is, how do I allow a user to make changes to the Album objects while also letting them delete them? I think I'd have to change the Array they are stored in, to a List. Then delete the object fromt the List, then change the List it back into the same Array. Is this correct? How would I do this if it is the right way to go about this?
Also, if it helps/makes a difference, the Array containing the Albums will be created in Form1, the Album objects will be deleted from Form 1, but the changes to the albumName in the Album object will happen in Form2.
Any help is much appreciated. I'm pretty new to using c#, so an example of the code to help me understand would be fantastic. Thank you.
Album class:
public class Album
{
public string albumName;
private int totalMusics;
private const int MAX_MUSICS = 100;
private Music[] myMusics;
public Album(string albumName)
{
this.albumName = albumName;
totalMusics = 0;
myMusics = new Music[MAX_MUSICS];
}
}
From what I understand of Lists (I haven't used them before this), I
can't put the Album objects into a List instead of an Array on
creation if I want to change a variable in the Album object later.
This is only true for structs. Since you are using a (mutable) class, you will be able to change the properties of the objects while they are stored in a List<Album>.
So you definitely should be using a List.
Unless you want to be able to look up Albums by some field, for example by AlbumName. If so, you could use a Dictionary<string, Album> and use AlbumName for the key.
I would suggest you store Music in List<Music> all along. Unlike other languages in C# List is actually a dynamically expandable array with constant indexing operation [].
Still keep in mind this implies that deleting any element from the list other than the last one would mean extra operations to shift the elements. If you don't care about the order of Music in the List I suggest before deleting an Album you swap it with the last one in the List and then delete the last element in the List.

Categories

Resources