Changing prefab instances through script - c#

I made a prefab gameObject with a rigidbody component attached. Whenever I change the mass of the original prefab from the inspector, all the instances present in the scene get affected (the field is not overridden). But when I try to do the same thing using a script, the mass of the instances remain unchanged (only the main prefab is affected and every time I enter play mode, it retains its previous value!). The script is attached to another gameObject and looks like this:
using UnityEngine;
using System.Collections;
public class CubeScript : MonoBehaviour {
public GameObject largeCube; // dragged the original prefab in the inspector
private Rigidbody rb;
// Use this for initialization
void Start ()
{
rb = (Rigidbody) largeCube.GetComponent ("Rigidbody");
rb.mass = 44; // this is not changing the instances, rather only the main prefab. Note that the mass is not overridden
}
}
I don't understand as a beginner. Please explain this to me.

I belieive that "Apply" button is responsible for applying all values to the instances of prefab at the inspector level. From the script, you need to do it manually (You don't have any button or method called "Apply").
The best (and most efficient in my opinion) is to create tag for prefab and the usage of
GameObject[] myPrefabInstances = GameObject.FindGameObjectsWithTag("yourTagName").
And then:
foreach (var go in myPrefabInstances)
{
var rb = (Rigidbody) go.GetComponent ("Rigidbody");
rb.mass = 44;
}

Related

How to avoid getting out of position when colliding with another GameObject?

In my Unity game I have some moving objects. There are some collectables ( triggers ) and whenever a moving object enters its trigger the collectable should
Replace itself with a moving object
Destroy itself
Unfortunately the current moving object collides with the new spawned moving object so it will be out of position ( very little ). I would like to avoid that, so one piece connects smoothly to the other one.
For reproduction purposes:
Moving
Create a cube GameObject
Add a Rigidbody component but disable the usage of gravity
Attach the following script to it
.
public class MoveForwardBehaviour : MonoBehaviour
{
private void FixedUpdate()
{
GetComponent<Rigidbody>().velocity = Vector3.forward; // just for testing purposes
}
}
Make this GameObject a prefab
Collectable
Create a cube GameObject
Add a Rigidbody component but disable the usage of gravity
Enable the collider trigger and modify the following values
Attach the following script to it
.
public class Collectable : MonoBehaviour
{
[SerializeField] private GameObject movingPrefab;
private void OnTriggerEnter(Collider other)
{
GetComponent<Collider>().enabled = false;
Instantiate(movingPrefab, transform.position, transform.rotation);
Destroy(gameObject);
}
}
Assign the moving prefab to the script
Make this GameObject a prefab
Now setup a sample scene like so
and start the game. After creating a new moving prefab you can see that the initial moving prefab is not in position anymore
I think this is because of the collision with the new instantiated prefab. Do you have any suggestions how to avoid this?

transfering a transform variable between two scritps in unity

public class player_movement : MonoBehaviour
{
// Start is called before the first frame update
public Transform position;
void Start()
{
position = GetComponent<Transform>();
}
how do I acsess the position variable in another script
There is no reason for any of this at all.
Your component player_movement is of type MonoBehaviour which is a Behaviour which is a Component and therefore already has the inherited property transform returning the Transform reference if the GameObject your component is attached to.
So whoever has a reference to your component immediately also has access to its .transform.position anyway.
As to how get that reference, there are thousands of different ways. The most typical ones
public player_movement playerMovement;
simply drag and drop it in vis the Inspector.
On another component attached to the same GameObject use
var playerMovement = GetComponent<player_movement>();
or if there is only one in the scene anyway
var playerMovement = FindObjectOfType<player_movement>();
either way in the end as said you can simply use
var playerTransform = playerMovement.transform;
and do with it whatever you want like accessing its position
var playerPosition = playerTransform.position;

Why my unity program froze when trying to create multiple gameobjects by c# script?

I have in the Hierarchy two ThirdPersonControllers.
And i create new c# script file then dragged the script to the first ThirdPersonController.
It should clone more 10 ThirdPersonControllers of the first ThirdPersonController.
This is the script:
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
void Awake()
{
gos = new GameObject[10];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos[i] = clone;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
In the Inspector of the ThirdPersonController in the script area in the Prefab i selected the ThirdPersonController.
But when running the game the Unity the whole program is frozed i had to use Task Manager to shut it down.
I know its the script the problem since i tried it without the script and it was fine.
What i want to do is instead 2 ThirdPersonControllers to have when running the game 10 or 20 ThiredPersonControllers each one in another position by using the c# script.
Screenshot of my Hierarchy:
From what I'm seeing looks like the prefab you are initializing has the "Multiple_objects" Script, so this means every new instance is going to execute that script and create 10 more and so on.
Try putting "Multiple_objects" Script on another GameObject thats not going to be dynamically initialized. (ex: An empty GameObject, Main Camera, Directional Light).

Score Count not working on a prefab

This is semi complicated of a question but I'll do my best to explain it:
I am making this mobile game in which you have to shoot four cubes. I'm trying to make it so when the cubes are shot by a bullet, they're destroyed and a UI text says 1/4, to 4/4 whenever a cube is shot. But it's being really weird and only counts to 1/4 even when all four cubes are shot and destroyed. I put these two scripts on the bullets (I made two separate scripts to see if that would do anything, it didn't)
And to give a better idea of what I'm talking about, here's a screenshot of the game itself.
I've been using Unity for about 6 days, so I apologize for anything I say that's noob-ish.
EDIT
So I combined the two scripts onto an empty gameobject and here's the new script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManagerScript : MonoBehaviour {
public GameObject cubes;
public Text countText;
public int cubeCount;
public Transform target;
// Use this for initialization
void Start () {
}
void OnTriggerEnter(Collider other)
{
cubes = other.gameObject;
}
// Update is called once per frame
void Update () {
cubes.transform.position = Vector3.MoveTowards(transform.position, target.position, 1f * Time.deltaTime);
if (cubes.gameObject.tag == "BULLET")
{
cubeCount = cubeCount + 1;
countText.text = cubeCount + "/4";
cubes.SetActive(false);
}
}
}
ANOTHER EDIT
I tried everything, so is there a way to detect when all the children in a parent on the Hierarchy are destroyed? Instead of counting up? This can give a better idea:
So I want to be able to detect when Cube, Cube1, Cube2, and Cube3 have all been destroyed.
The answer is pretty simple: Since every individual bullet has that script, each bullet has its own score.
For something like a score you want a single spot to store it, e.g. a script on an empty gameobject that serves as game controller. Just access that in the collision and increase the score (maybe have a look on singletons here).
You can combine those two scripts and actually it might be better to not have this on the bullet, but on the target because there are probably less of them which will save you some performance. (And it does more sense from a logical point of view.)
Edit:
I assume you create the bullets using Instantiate with a prefab. A prefab (= blueprint) is not actually in the game (only objects that are in the scene/hierarchy are in the game). Every use of Instantiate will create a new instance of that prefab with it's own version of components. A singleton is a thing that can only exist once, but also and that is why I mention it here, you can access it without something like Find. It is some sort of static. And an empty gameobject is just an object without visuals. You can easily create one in unity (rightclick > create empty). They are typically used as container and scriptholders.
Edit:
What you want is:
An empty gameobject with a script which holds the score.
A script that detects the collision using OnTriggerEnter and this script will either be on the bullets or on the targets.
Now, this is just a very quick example and can be optimized, but I hope this will give you an idea.
The script for the score, to be placed on an empty gameobject:
public class ScoreManager : MonoBehaviour
{
public Text scoreText; // the text object that displays the score, populate e.g. via inspector
private int score;
public void IncrementScore()
{
score++;
scoreText.text = score.ToString();
}
}
The collision script as bullet version:
public class Bullet : MonoBehaviour
{
private ScoreManager scoreManager;
private void Start()
{
scoreManager = GameObject.FindWithTag("GameManager").GetComponent<ScoreManager>(); // give the score manager empty gameobject that tag
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Target") == true)
{
// update score
scoreManager.IncrementScore();
// handle target, in this example it's just destroyed
Destroy(other.gameObject);
}
}
}

Unity3d MeshRender

I'm trying to use C# to disable and enable the MeshRender component in Unity3d however I am getting the following error,
error CS0120: An object reference is required to access non-static member `UnityEngine.GameObject.GetComponent(System.Type)'
The line of code I am using is below. I'm using this in the same function.
MeshRenderer showZone = GameObject.GetComponent<MeshRenderer>();
Also I'm posting here rather than Unity Answers as I get a far faster response here and it's always useful information regardless of the outcome.
You're having trouble with several problems. First, you are trying to use GetComponent<> on a class instead of an instance of an object. This leads directly to your second problem. After searching for a specific GameObject you're not using the result and you're trying to disable the renderer of the GameObject containing the script. Third, C# is case-sensitive, Renderer is a class while renderer is a reference to an instance of Renderer attached to the GameObject
This code snippet combines everything: find the GameObject and disable its renderer
GameObject go = GameObject.FindWithTag("zone1");
if (go != null) { // the result could be null if no matching GameObject is found
go.renderer.enabled = false;
}
You could use go.GetComponent<MeshRenderer>().enabled = false; instead of go.renderer. enabled = false; But by using renderer you don't need to know what kind of renderer is used by the GameObject. It could be a MeshRenderer or a SpriteRenderer for example, renderer always points to the renderer used by the GameObject, if there exists one.
My friend. Just try use lowercase gameObject instead of GameObject and renderer instead of Renderer
The main problem that you try access Static class variable, using the name of class instead of class instance.
Class names here are GameObject and Renderer
And instances are gameObject and renderer
MeshRenderer showZone = GetComponent<MeshRenderer>();
delete the 'GameObject.'
GameObject is a type. What you want is in an instance of a gameObject to call GetcComponent on. Thats what the error is about.
Which for note, this:
MeshRenderer showZone = GetComponent<MeshRenderer>();
is the exact same as this:
MeshRenderer showZone = this.GetComponent<MeshRenderer>();
You are calling GetComponent on the GameObject instance of which the script is attached to.
your code should look like this:
MeshRenderer showZone = GetComponent<MeshRenderer>();
Like others already wrote, you need to get an instantiated GameObject. You call the base class GameObject where only static functions can be called which do not need a GameObject in the SceneView.
gameObject IS AN instance.You get the instance of the GameObject the Monobehaviour is added to. Calling the function GetComponent without any object is the same as:
this
gameObject
GameObject IS NO instance.
Be careful at the first letter!
Look documentation:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
void Example() {
renderer.enabled = false;
}
}
Link: http://docs.unity3d.com/ScriptReference/Renderer-enabled.html
Changing of programming languages upper-right.
2 ways you can solve the Problem either. You add the word static to the method that is calling your statement.
ex :
public static GetTheMesh(){}
I do not recommend on doing this cause. If you have other calls inside the method that needs to access Instance, this will cause you trouble.
Second way of fixing it is make a pointer or reference first before getting the component. Or use the GameObject.Find <= which is slow if.
showZone = GameObject.Find("TheGameObjectName").GetComponent<MeshRenderer>();
If you want to disable the renderer on this gameObject then use:
this.GetComponent<Renderer>().enabled = false;
If it's not this gameObject then use:
GameObject.FindGameObjectWithTag("your_tag").GetComponent<Renderer>().enabled = false;
Or you could give the object manually:
public GameObject go;
go.GetComponent<Renderer>().enabled = false;
https://docs.unity3d.com/ScriptReference/Renderer-enabled.html
you can use two types of peace of code for access MeshRenderer enable and disable
1> create GetMeshRenderer script (script name as you want) attached to empty game-object into the scene and assign Cube or sphere or any 3d object as u want to enable and disable.
************************************** Code ***************************
using UnityEngine;
using System.Collections;
public class GetMeshRenderer : MonoBehaviour
{
public MeshRenderer ShowZone;
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if(Input.GetKey(KeyCode.Y))
{
ShowZone.enabled = true;
}
if(Input.GetKey(KeyCode.N))
{
ShowZone.enabled = false;
}
}
}
2>
attach below peace of code script to any 3d object like sphere ,cube
*************************** code ***************************
using UnityEngine;
using System.Collections;
public class GetMeshRenderer : MonoBehaviour
{
private MeshRenderer ShowZone;
// Use this for initialization
void Start ()
{
ShowZone = gameObject.GetComponent<MeshRenderer> ();
}
// Update is called once per frame
void Update ()
{
if(Input.GetKey(KeyCode.Y))
{
ShowZone.enabled = true;
}
if(Input.GetKey(KeyCode.N))
{
ShowZone.enabled = false;
}
}
}
Your problem is that you are using GameObject which is just a class that "describes" what it is. What you want, if this script is attached to the GameObject who's mesh renderer you want, is gameObject with a lowercase "g."
If you want to get the mesh renderer of another GameObject, you can find it by name with GameObject.Find("zone1"); (note the uppercase "G" in that one,) or you can give it a tag and find it with GameObject.FindGameObjectWithTag("zone1");
(You may or may not already know that but it doesn't hurt to provide the information.)
Edit:
Your other problem is that you must use renderer instead of Renderer because, like the GameObject "Renderer" with a capital "R" references a class, instead of a specific object.
the problem is GameObject is different from gameobject Gameobject is a class and gameobject is a instance of current gameobject or gameobject in which the script is attached
Replace the line
MeshRenderer showZone = GameObject.GetComponent<MeshRenderer>();
with
MeshRenderer showZone = gameobject.GetComponent<MeshRenderer>();
I think this will do,
Also note that in your Error statement ,it is saying that GameObject is a Class or a data type not an object
Do you see the gears? Yes? Click it and click remove component.
You can use the declaration:
other.gameobject.GetComponent< MeshRenderer>().Setactive (false);
One Line Reference...after the condition is fulfilled..
For more precise help regarding MeshRender, see the Unity Documentations..
I had same issue with my declaration and i just fixed it by changing "G" to "g" in gameObject and i declared it in Start so it is like...
MeshRenderer showZone = gameObject.GetComponent();

Categories