Is there any way to use AddComponent with a script in Unity if there is no instance of that script in the game yet? - unity3d

I need a way to add a script to an object that I just Instantiated. Ideally I could just set the script to add in the inspector. The problem is that I can't use
public Component scriptToAdd;
because then I would need to already have an empty object or something with the script on it. While that would work it feels kind of dirty or hackish to make an empty that just stores references to something. Also the point of doing it like this for me is to make it more efficient to swap out different scripts. If I had to swap out a script on an empty and then drag the new one in I'd lose on some of that efficiency I'm after. Thanks for any help.

After you instantiate, you should use the GameObject.AddComponent() Class, to add any component Type you want.
for example, if you want to add a camera to the scene.
void Start () {
GameObject myNewCamera = new GameObject();
//creates an empty game object, you can already attach components to it.
myNewCamera.AddComponent<Camera>();
//adds a component of type Camera
}
You can also use the overloaded constructor
GameObject myNewCamera = new GameObject("my object name", typeof(ScriptToAdd));
GameObject myNewCamera = new GameObject("my object name", typeof(ScriptToAdd), typeof(MoreComponentsToAdd));
Also, you should never be afraid of using an empty GameObject on your scene, for keeping information, references, variable values, saves, or anything really. It doesn't really affect the performance of your game, and you can make your life easy just by having an easy to find tag, such as "Engine".
Then you can access anything just by adding:
GameObject.FindGameObjectWithTag("Engine");
I hope I was able to help you :)
-Noe

Related

Why couldn't I use the "Resources.Load" result directly?

The Unity Manual says the Resources.Load returns the requested asset as an Object.I wonder why could't I use the returned Objectdirectly.For example,I have a Text prefab and I want to add it's instance to the Hierarchy,but the Code below won't work
Text prefab;
private void Start()
{
prefab = Resources.Load<Text>("Prefabs/Text");
GameObject canvas = GameObject.Find("Canvas");
prefab.transform.SetParent(canvas.transform);
}
I must Instantiate the return of the Resources.Load first like below
Text prefab;
private void Start()
{
prefab = Resources.Load<Text>("Prefabs/Text");
GameObject canvas = GameObject.Find("Canvas");
Text text = Instantiate(prefab);
text.transform.SetParent(canvas.transform);
}
I don't know what's the difference between the Instantiate result and Resources.Load result,and what the Instantiate do ,so that it's return can be added to Hierarchy.
Forgive my poor English!
To use the Method Instantiate(GameObject) you would write a new component, create a new variable of type GameObject, attach the component to an GameObject, and fill the variable in the inspector.
To use the Method Instantiate(Resource.Load("object path")) you just need the name/path of the Prefab.
this is extremely useful if you have a huge amount of generated parts in your game (so there are no gameobjects placed in the editor), if you'd want to avoid Resource.Load you'd need some "data-holder-gameobject" placed in an nearly empty scene. edited to make my point a bit clearer
it is aswell helpfull if you have large number of different Prefabs and your method knows the name of the object it wants to build, or you just simply dont want to drag and drop all those prefabs into the inspector window
Resource.Load, loads data from your drive. it's possible that your game is played from a Hard drive, which would mean to load the prefabs the hard drive needs to rotate, position the read-head, and so on.
Instantiate is slow itself even without the need of Resource.Load Instantiate is not that fast. if it happens that you need it very often ( multiple times per second) you should consider some kind of object-pool 1

Having a list with a script and getting the gameobject from it

I have a script that has a list with the type being a certain script. Then wanting to instantiate all of the members of this list (GameObjects have this script). I've tried doing a for loop over the list and then doing .GetComponent<GameObject>(); but of course that doesn't work for obvious reasons. So Is there a way to do this?
For anyone wondering why I'm trying to do this, I want to limit anyone else that is using this script to only add gameobjects with this script (since I don't want gameobject without this script being spawned)
Maybe having a reference on the certain script that knows what gameobject has been attached to it? or is there a better way to do this
Thank you :)
It would be easier if you added your code to the question.
Well, GameObject is obviously not of type Component (MonoBehaviour inherits from Component).
It sounds like what you have is a List<SomeComponent>.
So you can simply access the property Component.gameObject which is the reference to the GameObject that component is attached to.
List<SomeComponent> list;
...
foreach(var component in list)
{
var obj = component.gameObject;
...
}
Note however that you actually don't need this. It sounds like you want to pass the elements of the list to Instantiate which also takes a Component (or better said any Object the mother class of GameObject, Component and ScriptableObject) as prefab parameter. It then returns the same type of the given prefab.

Unity3d, add component to prefab in resource

One time I was playing around with Resouce system in Unity I discovered this:
Say if you only load but do not Instantiate the prefab:
GameObject testObj = Resource.Load("testPrefab");
And you have a script called "TestScript", and you add this script to testObj
testObj.AddComponent<TestScript>();
You run it, and close it, this TestScript will still be inside testPrefab inside Resource folder, and if you run it again, the testPrefab in Resource folder will have two TestScript component.
I understand that Resouce.Load() is just point to that prefab in Resource folder, no copy has occured.
But would it be a good idea says, put a CleanPrefab() function in this prefab to make sure it restore to the original configuration first and then add other conponents as needed?
Do not modify a prefab. Prefabs are made to be used as re-usable Objects. There are many reasons you shouldn't try to modify it. One of them is that it will affect instantiated objects references that prefab. With modifications done to your prefab, there is no guarantee your levels will start the way they are supposed to start. This will mess up your game.
You run it, and close it, this TestScript will still be inside
testPrefab inside Resource folder, and if you run it again, the
testPrefab in Resource folder will have two TestScript component.
The behavior you described is only true in the Editor. One you build it, the behavior is totally different. In a standalone version, once you restart the program, the prefabs will automatically reset itself. What you are really modifying is the loaded prefab in the memory not the one stored on the disk. Also note that everything in the Resources folder is read-only. They can't be modified.
If you modify a prefab directly, it stays in the memory even when you reload the scene. The only time that modification will be gone is when you restart the application. Do not modify a prefab.
As mentioned here, Resources.Load doesn't create a new instance of an object because it does not create an Object in the scene; that's what Instantiate does.
To avoid this problem of linking to the original prefab and avoid overriding, use Instantiate() and then add all your components with respect to that variable.
It is important to use Instantiate before apply new components to the object or modify the existing ones.
For instance you have this snippet of code:
void Start()
{
SelectCharacter();
InstantiateObj();
ControllerAssignment();
AddColliderComponent();
AddScriptComponent();
}
private void InstantiateObj()
{
obj = Instantiate(obj_original_prefab); //apply changes to this variable
}
This code is a correct implementation, where you call the Instantiate method before applying all the changes to the object obj, which is a 'deep copy' of your original prefab and not a reference!

Talking to GameObject and components, what is the logic?

I am trying to attain the legendary skill of mastering how to make my scripts talk with any GameObject and their components wherever they are. To do that, i watched a couple of tutorials like https://www.youtube.com/watch?v=LrkfSqxz4jU, but my brain still seem to resist to smartness :(.
So far, i have understood than in order to do that i first need my
script to find the right gameobject in my scene (if the script is not
attached directly to it), and assign it to a variable, with for
example:
myVariable = GameObject.Find ("MyGameObjectName");
Then, when i have found this gameobject (and eventually summoned it if it was not in my scene), i find myself at loss to figure out how to call the right component (and inherently, how to call the right sub-element.
For example, i have at the moment a game object for my UI with :
RectTransform, CanvasRenderer,UI Controller (Script),Grid Layout Group (Script)
In order to modify the RectTransform 's Pivot X for example, my logic tells me to add to my script:
myVariable.GetComponent<RectTransform> ();
myVariable.RectTransform.Pivot.x = 0.75;
...Which get all red and bad, and i don't understand why. I am also not knowing how i am supposed to call the component GridLayoutGroup. I suppose there is a even dirtier trick in the sense that it is written (script)...
To give you another example that i find confusing, if i would type myVariable.transform.position.x , is it changing the RectTransform, or another hidden transform that i don't know of ?
It is confusing because i would think that logically, this should be called instead myVariable.RectTransform.position.x or something.
So the point of all that is: What's the big idea ? What is the core concept that i am missing ?
I am confused ! :D
Because having public fields is bad practice (accessible from everywhere) you should use serialized fields.
I'll just use the example Vancete made up, but with a serialized field instead of a public field.
[SerializeField] GameObject myGo; // a space to drop a GameObject will appear in the inspector too,
// with the benefit of not having a public field
// (not specifying public, private, protected etc. makes the field private in C#)
void Start() {
Image myImg = myGo.GetComponent<Image>();
myImg.sprite = // WHATEVER
myImg.color = // YOUR PREFERRED COLOR
}
At the bottom of this page, you'll find a nice table, comparing these modifiers.
These people here are all proving my concept. As the user, who wrote the last answer on the linked page, points out, even Unity is using [SerializeField] in their example project.
You'll find another proof in this article.
The fastest and best way to access a GameObject is declaring it as public and drag&dropping in the inspector.
GameObject.Find is slow (since it requires a tree search) and impractical (you will have problems if you rename the GameObject or change its hierarchy), things that can be avoided linking it in the mentioned way above.
For example, using GameObject.Find inside the Update is a real performance killer.
Also, if you are going to access to a GameObject component more than once, it's recommended to reference it before using it.
public GameObject myGo; // a space to drop a GameObject will appear in the inspector
void Start(){
Image myImg = myGo.GetComponent<Image>();
myImg.sprite = // WHATEVER
myImg.color = // YOUR PREFERRED COLOR
}

How to keep references to UI elements in a prefab, instantiated at runtime

I have a prefab, which has a script component as MonoBehaviour. This script has 3 public fields as text; which are assigned in the inspector, before saving the gameobject and remove it from the scene.
Now, this works fine if I have a UI element, like a panel. Every text field on the panel, defined in the prefab, is still assigned when the prefab is instantiated at runtime.
This sadly does not work on another prefab that I have made; which is not a UI element. In my case it is a meshgameobject with various components on it (navmesh agent, capsule collider, rigidbody, animator and so on)
I believe this is due the fact that with the UI panel, the elements are already in the gameobject hierarchy, while when the reference is on a different gameobject; Unity does not keep track of them.
This means that I have to always add at runtime via code, the elements that I want to reference on each prefab, if they are not part of the game object itself? In this case I would just avoid to have my references public then, since I won't be using the inspector in this case (every gameobject of this type is instantiated at runtime).
Just checking if there is any other solution to work around this.
Use List to save reference for each generated object.
Lets assume that the object you want to add to store the reference is called meshgameobject.
Example:
List<meshgameobject> meshGOB;
initiaize inside Start function
void Start(){
meshGOB = new List <meshgameobject>();
}
Then create and use list.add() to add reference during runtime like
meshGOB.Add((meshgameobject)Instantiate(prefab, Pos, Quaternion.Identity);
OR
meshgameobject tempGOB= (meshgameobject)Instantiate(prefab, Pos, Quaternion.Identity);
//Do something with meshgameobject
tempGOB.someAction......
then add the reference to the List
meshGOB.Add(tempGOB);
Do not make a perfab which references to other gameobjects in the scene but are not in the hierarchy of the perfabI itself. AFAIK, Unity can not track this kind of references.
Even if Unity supported this kind of prefab, you could not use this kind of prefab in other scenes because the specific references, so it is not make much sense to make a perfab like that. If you have to make it a pefab just because the scene and the game object are maintained by different person, you may use Find() to populate the references at runtime.