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.
Related
I have a script for that Instantiates 2 game object but when something happens to one of them it also happens to the another one even when the conditions are not met for it. How can I make the script act separately for every Game Object?
GO script:
private Transform target;
public float speed = 2f;
private Animator anim;
public float H2Damage = 5f;
private healthBar Hp;
void Start()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
anim = gameObject.GetComponent<Animator>();
Hp = GameObject.FindGameObjectWithTag("enemy").GetComponentInChildren<healthBar>();
}
void Update()
{
target = GameObject.FindGameObjectWithTag("enemy").GetComponent<Transform>();
if (Hp.Died == true)
{
Hp.Died = false;
anim.SetBool("Hero2Attack", false);
anim.SetBool("Hero2Move", true);
}
if (!target || this.anim.GetCurrentAnimatorStateInfo(0).IsName("Hero2ATTACK"))
return;
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
anim.SetBool("Hero2Move", true);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("enemy"))
{
anim.SetBool("Hero2Attack", true);
healthBar hp = col.transform.GetComponent<healthBar>();
hp.GiveDamage(H2Damage);
}
}
I believe they act the same way because they are getting the same GetComponentWithTag(), so they will Get the same objects. You also instantiate the animator, which is the exact same one, so they will do the same things. --> If it was as simple as changing the position by 1 meter per second, they would have different behavior (ie. be at different positions) But.... If you instantiate it, the current position is also instantiated, so you are going to have to get the current postion and set it to what you want, so both of these moving boxes aren't at the exact same position. You will have to do something similar here, you are going to want to either (create different scripts for the objects, or set the script values to different things)
TL;DR: You are copying the exact same script with the exact same components, so they will act similarly.
Fix: The only way to fix this is by setting their values after you instantiate them using GetComponent<>()or changing the script you assign them. (or any components for that matter)
Let me know if you have any problems or questions in the comments! :)
I want to make a robot on unity3D. I want to make the gripper of the robot when collide with object attach the gripper. So the object will follow the gripper.
What will be added to this script in order to make some thing like this?
private Rigidbody gripper;
void Start()
{
gripper_part01 = GetComponent<Rigidbody>();
}
void Update()
{
if (Input.GetKey("a"))
gripper.AddForce(transform.forward * 100);
}
void OnCollisionEnter(Collider obj1)
{
// how to make obj1 follow the gripper
}
It can be done by various methods. But the simplest would be making the obj1 child of gripper as soon it collides.
code will look some thing like this
void OnCollisionEnter(Collider obj1)
{
// how to make obj1 follow the gripper
obj1.transform.parent = gripper.transform;
}
Look into the state machine pattern
http://gameprogrammingpatterns.com/state.html
You'll want to create 2 states:
Not Following(Default)
Following
When the player enters the collider, switch the state to following. I'm not going to write you an entire state machine/state switching architecture for you to copy & paste, but I can tell you that you're going to want to make the states themselves a MonoBehavior class, and the actual StateMachine will not have any parent classes and will be attached to the object's controllers.
Then, you'll want to keep track of the player's coordinates like so
Vector3 playerPosition;
void OnCollisionEnter(Collider obj1)
{
if(obj1.gameObject.tag == "Player"){
playerPosition = obj1.transform.position;
myStateMachine.switchState("Follow"); //obviously replace this with your own state machine code
}
}
Make sure the player's gameObject actually has the "Player" tag in the Unity editor.
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));
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 having an issue respawning a prefab after it has been destroyed. I can't seem to get it to respawn back at its original start position after a second of being destroyed. I have created an empty game object and attached the SpawnTargets.cs script to it. I'm not sure of what the best methodology to approach this situation. Another object with a script attached to it does the actual destroy of the prefab. BulletCollisionHandler.cs works fine though. Thanks for any help. Code is below:
SpawnTargets.cs:
using UnityEngine;
using System.Collections;
public class SpawnTargets : MonoBehaviour
{
public GameObject targetCircle;
public GameObject targetSquare;
public GameObject targetStar;
private Vector3 circleSpawnPosition = new Vector3(0.0f, 1.227389f, -7.5f);
private Vector3 squareSpawnPosition = new Vector3(0.0f, 1.027975f, -7.993299f);
private Vector3 starSpawnPosition = new Vector3(0.0f, 1.8f, -7f);
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
SpawnTarget ();
}
void SpawnTarget()
{
}
}
BulletCollisionHandler.cs:
using UnityEngine;
using System.Collections;
public class BulletCollisionHandler : MonoBehaviour
{
public GameObject targetCircle;
// Use this for initialization
void Start ()
{
Destroy (gameObject, 2);
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.name == "TargetSquare")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit square");
}
else if(other.gameObject.name == "TargetCircle")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit circle");
}
else if(other.gameObject.name == "TargetStar")
{
other.gameObject.rigidbody.isKinematic = false;
((TargetMovementHorizontal)other.gameObject.GetComponent<TargetMovementHorizontal>()).enabled = false;
((TargetMovementVertical)other.gameObject.GetComponent<TargetMovementVertical>()).enabled = false;
Destroy (other.gameObject, 1);
Debug.Log("Hit star");
}
}
}
You're not calling Instantiate() anywhere, so it's hard to see where the new object would come from in the code you've supplied.
In any case, it might be better not to use Destroy. If you want to immediately reset the object, why not simply recycle it back to the start position? It's a good idea to avoid instantiating and destroying lots of objects, it's better to hide/disable the ones your don't need and unhide/re-enable them.
Here's a tutorial on the general idea. The tutorial is about groups of objects but the same trick would work for recycling single objects too.
You are better of using gameObject.SetActive( true/false ); for activating / deactivating the gameObject instead of just using Destroy.
Then if you are using Destroy you have 3 options that comes to mind for getting it into the desire position before the Player sees it.
1) You enable the game object after disabling its Renderer component. Then you equalize the transform's position / rotation the one you need. After this you re-enable the Renderer component. It should be placed where you want it.
2) You Instantiate the gameObject, but first making sure the Renderer component is disabled on its Prefab, by default, so you can re-assign its Transform values then - re-enable the Renderer again.
3) You make an invisible gameObject (an Empty gameObject) and Instantiate the wanted gameObject, you then make the Empty to be the parent of the newly created gameObject.. Provided that the parent Empty is exactly where you want it to be, when you instantiate and reset the child's position it should jump off right on top the the Empty parent.
I'm not giving code since you haven't and I don't have no idea of which method you might end up liking more. In terms of performance the Enable/Disable are the best option.
And as theodox says Object Pooling is your best friend for things like bullets, although it might be applied to many other gameObjects that might work as 'collections of objects' on your game's logic. It's totally worth learning.