I have a Script in my main Assets folder called BezierWalk.cs
In another script, Spawn, I'm trying to instantiate objects from Prefab Sphere and attach BezierWalk.cs to them.
Spawn script:
public class Spawn : MonoBehaviour
{
public GameObject Sphere;
//string ScriptName = "BezierWalk";
void Start()
{
Vector3 vSpawnPos = transform.position;
for (int i = 0; i < 20; i++)
{
var objectYouCreate = Instantiate(Sphere, vSpawnPos, transform.rotation);
//objectYouCreate.AddComponent<T>("Assets/BezierWalk.cs");
//objectYouCreate.AddComponent(typeof(ScriptName)) as ScriptName;
//objectYouCreate.AddComponent<ScriptName>();
//var myScript = Sphere.AddComponent<BezierWalk.cs>();
vSpawnPos.z += 20;
}
}
You can see commented out attempts...
How I am supposed to do this properly? Thank you.
If you look at how you reference components in unity, the answer should be clear - did you try any of the ones you listed?
Reference: https://docs.unity3d.com/ScriptReference/GameObject.AddComponent.html
The normally used is as it is easy reading
objectYouCreate.AddComponent<ClassName>();
You can use
objectYouCreate.AddComponent(typeof(ClassName));
the .cs is for humans, its your readable version. so you would never need the .cs reference in your code.
note: I mentioned it as ClassName rather than scriptname, as while they are the same in monobehaviors in unity, it isnt the same anywhere else in c# so, the important bit is not the name of the file you made, but the name of the class within it.
Another way is to have prefabs, make a prefab of your object with all the components you need already on it.
If your script lies within certain namespace, you should follow the following format
GameObject.AddComponent(typeof(namespace.className));
Related
I am instantiating a prefab thru a script in Unity. It's kind of working but not really.
The prefab shows up in the hierarchy with the correct name but in the scene, it cannot be seen.
I have looked through every question I saw, that was similar but unfortunately, I couldn't solve my problem. I tried changing the Vector in the script and the position of the prefab itself.
I appreciate any help.
public class ButtonControler : MonoBehaviour
[SerializeField] private Button[] buttonsList;
public GameObject button;
public void Awake()
{
InitializeButtons();
}
private void InitializeButtons()
{
for (int i = 0; i < CsvManagerDownloader.experiments.Count; i++)
{
string buttontext = "Experiment" + i;
GameObject newButton = Instantiate(button, new Vector3(0, 0, 0), Quaternion.identity);
newButton.GetComponentInChildren<Text>().text = buttontext;
}
(This script can only be called after CSVManagerDownloader, since the number of buttons is gathered from that script.)
It seems that you are trying to instantiate an object that is supposed to be used inside Canvas (unity UI system) in the world space.
Canvas component on the root object is needed to render all child UI elements like Button, Text, Image, etc.
So, if you want your instantiated object with the Button component to be visible, make sure, that you place it in the hierarchy with the Canvas component attached to any of its parents.
Also, make sure that the EventSystem component is present at the scene to make Buttons work.
bool isRight = collision.GetComponent<Paddle>().isRight;
I have a script called Paddle.cs attached to a gameObject Paddle, Paddle.cs contains a field 'bool isRight;'.
This part of the code detects which object is colliding with the current object.
I don't understand this part
collision.GetComponent<Paddle>().isRight;
Here is my script
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "Paddle")
{
bool isRight = collision.GetComponent<Paddle>().isRight;
if (isRight)
{
direction.x *= -1;
}
if (!isRight)
{
direction.x *= -1;
}
}
}
Because in Unity both exist: GameObject.GetComponent and also Component.GetComponent
Collider2D as well as MonoBehaviour inherit from Behaviour which inherits from Component
Internally it is basically kind of a shortcut and afaik behaves exactly the same so it makes no real difference whether you use
collision.GetComponent<XY>()
or
collision.gameObject.GetComponent<XY>()
That's also the reason why you usually simply use
var someComponent = GetComponent<SomeComponent>();
in your scripts instead of having to go through
var someComponent = gameObject.GetComponent<SomeComponent>();
in general: Since there is also OnCollisionEnter using the class Collision just to avoid confusions you should rather call your parameter Collider2D collider or usually (in the docs) it is other.
We use Component.GetComponent as a shortcut for GameObject.GetComponent.
It allows you to get a component on the same GameObject the script is attached to without having to explicitly reference the object itself. In other words, simplifying the code from
var someComponent = gameObject.GetComponent<SomeComponent>();
to
var someComponent = GetComponent<SomeComponent>();
The two methods are functionally identical.
You can find the source-code in the UnityCsReference page on github, although, as I understand it, it's just a wrapper for native (C++, platform-specific) code. Specifically, it's calling GameObjectBindings::GetComponentFromType.
I have made my own component which conflicts with some Unity built-in components (like Rigidbody conflicts with Rigidbody2D). So I need to be sure that those components will not exist together in the same GameObject. Is there a way to do it? It seems to be easy to check when my own component is added (by Reset), but what to do if Unity' built-in component is added? Is there some callback, message, or event sent when new component attached to the GameObject?
Precisions
I do not need to hide components it in the editor, or prevent adding my own components. I am asking about preventing adding certain Unity' built-in components while my component is attached. From both Editor GUI (by "add component" button) and Unity API (by GameObject.AddComponent).
There is the [DisallowMultipleComponent] attribute which prevents two of the same type from being added to the same game object. This works for subtypes as well (which is how Rigidbody and Rigidbody2d are handled).
I am not sure if this will work for you or not, as you haven't said your components are related to each other, but it is what I can find.
Is there some callback, message, or event sent when new component
attached to the GameObject?
No.
Is there a way to do it?
Yes, but a bit complicated.
If you want to prevent your custom script from being added, that would have been easy and this question should handle that.
This is complicated because you want to prevent a component written by another person(built-in) from being added to a GameObject which means that you first need a way to detect when that component has been added to a GameObject then destroy it. This has to be done every frame (Both in the Editor and during run-time).
You can call the components you don't want to be added to a GameObject blacklisted components.
Here are the steps:
1.Store the blacklisted components in an array.
private static Type[] blacklistedComponents =
{
typeof(Rigidbody),
typeof(Rigidbody2D)
//...
};
2.Get the root GameObjects in the scene and store them in a List.
private static List<GameObject> rootGameObjects = new List<GameObject>();
Scene.GetRootGameObjects(rootGameObjects);
3.Loop through each root GameObject and use GetComponentsInChildren to get all the components attached to each GameObject under that root GameObject.
private static List<Component> allComponents = new List<Component>();
currentLoopRoot.GetComponentsInChildren<Component>(true, allComponents);
4.During the loop from #3, loop through the retrieved components and check if it has any blacklisted component. If it does, destroy that blacklisted component.
for (int i = 0; i < allComponents.Count; i++)
{
//Loop through each blacklisted Component and see if it is present
for (int j = 0; j < blacklistedComponents.Length; j++)
{
if (allComponents[i].GetType() == blacklistedComponents[j])
{
Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
Debug.Log("Removing Blacklisted Component");
//Destroy Component
DestroyImmediate(allComponents[i]);
Debug.LogWarning("This component is now destroyed");
}
}
}
That's it. You or others may have few questions about this answer.
Q 1.Wonder why FindObjectsOfType and FindObjectsOfTypeAll are not used?
A 1.These functions are usually used to simplify getting everything in the scene but the problem is that they return array. Calling these functions every frame will kill your game performance since it allocates memory and will cause garbage collector to run more often.
This is why Scene.GetRootGameObjects is used which you can pass a List inside it and it will fill the list for you. It does not return array.
Q 2.Why did you pass List to GetComponentsInChildren and not return the result from it?
A 2. Technically the-same reason I explained above. I used a version of the GetComponentsInChildren function that does not allocate memory. Simply pass List to it and it will fill it up with every component it found. This prevents it from returning an array which is expensive.
I wrote a complete working code for this below but you need to improve it. That's why I explained every process so that you can either improve or rewrite it yourself. It currently prevents Rigidbody and Rigidbody2D from being added from the Editor or from code in the Editor or in a build.You can add more components you want to block to the blacklistedComponents variable. It runs in the Editor also during runtime. UNITY_EDITOR is used to remove the Editor codes and make sure that it compiles for platforms.
1.Create a script called ComponentDetector and copy every code below into it.
2.Save and go back to the Editor. That's it. You don't have to attach it to any Object. You should never be able to add Rigidbody and Rigidbody2D to any GameObject.
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class ComponentDetector : MonoBehaviour
{
//Add the blacklisted Components here
private static Type[] blacklistedComponents =
{
typeof(Rigidbody),
typeof(Rigidbody2D)
//...
};
private static List<Component> allComponents = new List<Component>();
private static List<GameObject> rootGameObjects = new List<GameObject>();
private static void GetAllRootObject()
{
Scene activeScene = SceneManager.GetActiveScene();
activeScene.GetRootGameObjects(rootGameObjects);
}
private static void GetAllComponentsAndCheckIfBlacklisted()
{
for (int i = 0; i < rootGameObjects.Count; ++i)
{
GameObject obj = rootGameObjects[i];
//Debug.Log(obj.name);
//Get all child components attached to this GameObject
obj.GetComponentsInChildren<Component>(true, allComponents);
//Remove component if present in the blacklist array
RemoveComponentIfBlacklisted(allComponents, blacklistedComponents);
}
}
private static void RemoveComponentIfBlacklisted(List<Component> targetComponents, Type[] blacklistedList)
{
//Loop through each target Component
for (int i = 0; i < targetComponents.Count; i++)
{
//Debug.Log(targetComponents[i].GetType());
//Loop through each blacklisted Component and see if it is present
for (int j = 0; j < blacklistedList.Length; j++)
{
if (targetComponents[i].GetType() == blacklistedList[j])
{
Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
Debug.LogError("You are not allowed to add the " + targetComponents[i].GetType().Name + " component to a GameObject");
Debug.Log("Removing Blacklisted Component");
//Destroy Component
DestroyImmediate(targetComponents[i]);
Debug.LogWarning("This component is now destroyed");
}
}
}
}
public static void SearchAndRemoveblacklistedComponents()
{
//Get all root GameObjects
GetAllRootObject();
//Get all child components attached to each GameObject and remove them
GetAllComponentsAndCheckIfBlacklisted();
}
void Awake()
{
DontDestroyOnLoad(this.gameObject);
}
// Update is called once per frame
void Update()
{
//Debug.Log("Update: Run-time");
SearchAndRemoveblacklistedComponents();
}
}
#if UNITY_EDITOR
[InitializeOnLoad]
class ComponentDetectorEditor
{
static ComponentDetectorEditor()
{
createComponentDetector();
EditorApplication.update += Update;
}
static void Update()
{
//Debug.Log("Update: Editor");
ComponentDetector.SearchAndRemoveblacklistedComponents();
}
static void createComponentDetector()
{
GameObject obj = GameObject.Find("___CDetector___");
if (obj == null)
{
obj = new GameObject("___CDetector___");
}
//Hide from the Editor
obj.hideFlags = HideFlags.HideInHierarchy;
obj.hideFlags = HideFlags.HideInInspector;
ComponentDetector cd = obj.GetComponent<ComponentDetector>();
if (cd == null)
{
cd = obj.AddComponent<ComponentDetector>();
}
}
}
#endif
If you're trying to determine if the component exists before runtime, which I assume you already know, you can use the Start() method. But, like I said, I assume you already know that, so the only other way to check something like that during runtime would be to continually check for it in the Update() method, every frame. Although, I am not sure as to why a Unity component might be added to a gameobject during runtime. If this is an issue, maybe the component in question could be added to a child or parent gameobject instead?
If you really feel like making some changes, which may require a lot of refactoring for your project, you could always create a ComponentManager class that handles adding and removing components to GameObjects and create your own callbacks.
I am trying to create button in C# code using an existing Prefab. All I found on the website was something like
public GameObject a;
public RectTransform ParentPanel;
void Start()
{
for (int i = 0; i < 5; i++)
{
GameObject goButton = (GameObject)Instantiate(a); /error
goButton.transform.SetParent(ParentPanel, false);
goButton.transform.localScale = new Vector3(1, 1, 1);
Button tempButton = goButton.GetComponent<Button>();
}
}
In Unity project I have Prefab called "ButtonPrefab" where I put Button Object. There is an error in below line
UnassignedReferenceException: The variable a of NewBehaviourScript has not been assigned.
You probably need to assign the a variable of the NewBehaviourScript script in the inspector.
I am new to Unity. How is it come that not assigned var a can give me button using Instantiate()? And why do I have this error?
You can instantiate prefabs very easily from within a Resources folder. Make a new folder within your project's Assets folder called Resources. It must be named Resources for this to work properly. Then you can make your button like this:
GameObject button = Instantiate(Resources.Load("myButton", typeof(GameObject))) as GameObject;
Assuming your prefab is named "myButton."
For context, I have instantiated multiple GameObjects over time which represent the score in my game. I know how to change the position where they are instantiated.
string scoreText = score.ToString ();
for (int i = 0; i < scoreText.Length; i++)
{
var go = (GameObject)Instantiate(Resources.Load(scoreText[i].ToString()));
go.transform.localPosition = new Vector3(0.02F + i * 0.01F, 0.13F, 0);
}
What I don't know how to do is change the position in another script. Again, for context, on Game Over in the other script, I want to change the position of these objects I instantiated.
I have tried multiple variations of GetComponent and GameObject. I can't pin down one GameObject that I want to access, because it is multiple, ever changing GameObjects. I feel like I am not looking at this right. Does anyone know how to do this?
Well one way to go would be to make your GameObject variable go a class variable so that you can access it in other methods. But if you spawn many objects you can make an array of GameObjects and then iterate through them with to find one you need. You can make the array a public variable so that other scripts can look at it.
The other scripts can access the data of your script that is public and static, so the first step is to create a variable that will hold your score in the main script:
public static GameObject[] Score;
During the initialization, use this variable to save the score:
string scoreText = score.ToString ();
Score = new GameObject[scoreText.Length];
for (int i = 0; i < scoreText.Length; i++)
{
Score[i] = (GameObject)Instantiate(Resources.Load(scoreText[i].ToString()));
Score[i].transform.localPosition = new Vector3(0.02F + i * 0.01F, 0.13F, 0);
}
At this point, inside the game over script, you should be able to use the score data from the main script (if this doesn't work, make sure you are using the right namespaces and references, but if the two scripts are in the same project there shouldn't be any problems):
MainScript.Score //MainScript is the name of your main script
If you want to iterate over the score in the game over script:
for (int i = 0; i < MainScript.Score.Length; i++)
{
MainScript.Score[i] = do something here...
}