Changing an object change all the instantiated objects - c#

I know that the topic of the question has been already asked and some answer have been given to it, but i'm asking it again because i fail misserably when i try to apply the answers from the other topics.
The problem that i have is that, i have a prefab (quad) 'Star' with a material into it and an script that makes it change color after some time:
using UnityEngine;
public class Star: MonoBehaviour {
public enum StarType {
BRIGHT,
DARK,
F,
K,
L,
P
}
private Ship ship;
private StarType starType;
private float timeLimit = .30f, timer;
private System.Random rand;
public void Awake() {
ship = FindObjectOfType<Ship>();
timer = .0f;
rand = new System.Random();
}
public void Start() {
refactor();
}
public void Update() {
timer += Time.deltaTime;
if(timer > timeLimit) {
refactor();
timer = .0f;
}
}
public void LateUpdate() {
transform.eulerAngles = new Vector3(ship.transform.eulerAngles.x,
ship.transform.eulerAngles.y,
ship.transform.eulerAngles.z);
}
public void refactor() {
starType = (StarType) rand.Next(0,
System.Enum.GetValues(typeof(StarType)).Length);
switch(starType ) {
case StarType.BRIGHT:
renderer.material.color = Color.white;
break;
case StarType.DARK:
renderer.material.color = Color.black;
break;
case StarType.F:
renderer.material.color = Color.red;
break;
case StarType.K:
renderer.material.color = Color.cyan;
break;
case StarType.L:
renderer.material.color = Color.green;
break;
case StarType.P:
renderer.material.color = Color.magenta;
break;
}
}
public StarType getStarType() {
return starType;
}
}
And this stars being instantiated in a Chunk class:
public class Chunk : MonoBehaviour {
public Star star;
private IList<Star> stars;
public void Awake() {
stars = new List<Star>();
Star auxStar = (Star) Instantiate(star, transform.position,
Quaternion.identity);
auxStar.transform.SetParent(transform);
stars.Add(auxStar);
}
}
I was assuming that each star would change the color independently, but instead of that they all change to the same color.
I've tried using sharedMaterial instead of material as i read it on some asnwers, though the result and behaviour seems the same?, and i tried giving random init colors to the stars, but the most that i get is having a few of them changing to a color different from the rest, though still the same between them (And i'm trying to change colors all the time, not only upon creation).
I've read too creating a material and asigning to each one at instantiating, but i had no luck:
auxStar.renderer.material = (Material)
Instantiate(star.renderer.material);
Does someone know how can the problem be handled?

According to Unity API reference, you are doing the right thing.
The issue in your code is that you are instantiating a MonoBehaviour
public Star star;
...
Star auxStar = (Star) Instantiate(star, transform.position, Quaternion.identity);
where star is a MonoBehaviour, you are not instantiating the actual GameObject with the Mesh Render and/or Material. Instead make sure you are instantiating the right GameObject Prefab, not just the component, like this:
public GameObject starPrefab;
...
GameObject starClone = (GameObject) Instantiate (starPrefab);
Star auxStar = starClone.GetComponent<Star> ();
In that way you have control of the GameObject, if you need, and every component associated with the Cloned Object, in this case Star. You don't have to instantiate the Prefab Material cause it will be already instantiated.
Here is a simple Example:
using UnityEngine;
using System.Collections;
public class SpawnStar : MonoBehaviour {
public GameObject StarPrefab;
public bool InstantiateStar = false;
void Update () {
if (InstantiateStar) {
GameObject starClone = (GameObject)Instantiate (StarPrefab);
float r = Random.Range (0f, 1f);
float g = Random.Range (0f, 1f);
float b = Random.Range (0f, 1f);
float a = Random.Range (0f, 1f);
starClone.renderer.material.color = new Color (r, g, b, a);
InstantiateStar = false;
}
}
}
Side Note: Make sure to not modify the Prefab during gameplay since is the source of all other cloned objects.

Try using Random.Range() As in System.Random is generated by system time. As we know that Time is not Really Random. It still depends on mil sec times and generate the the logic of Random as we can't really tell which mil sec the random will run.
Since System.Random runs on 1 timeline which your scripts are using. Random.Range() is a bet you can consider since it uses SystemTime and independent time On Run of the platform. Shares with Time.DeltaTime.

Related

Unity 2D C# - Parenting not working question for my game

So I am trying to make a top-down tycoon game where a truck gives you crates and you sell the crates for money. I have a script for the trucks and it's working just fine except for the first part. So I coded it that when the trucks are spawned 2 crates spawn and are parented to the truck game object, and the issue is that when I run the game, the objects spawn and all, but they aren't parented to the trucks, I have 6 trucks in the scene and only one gets their crate parented. Can someone help?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CarScript : MonoBehaviour
{
private float speed = 0.01f;
private Vector3 dropoffLocation;
private bool hasCrates = true;
private float dropCratesCooldown = 500f;
GameObject cratess;
private float crateUpgrade = 1;
public GameObject crateUpgrade1;
public GameObject crateUpgrade2;
public GameObject crateUpgrade3;
public GameObject crateUpgrade4;
private void Start()
{
dropoffLocation = new Vector3(3.5f, -7f, 0);
crateUpgrade1 = GameObject.Find("Boxes In Truck 1");
crateUpgrade2 = GameObject.Find("Boxes In Truck 2");
crateUpgrade3 = GameObject.Find("Boxes In Truck 3");
crateUpgrade4 = GameObject.Find("Boxes In Truck max");
if (crateUpgrade == 1)
{
Instantiate(crateUpgrade1);
crateUpgrade1.transform.parent = transform;
//crateUpgrade1.transform.position = new Vector3(0, 0, 0);
}
}
// Update is called once per frame
void Update()
{
if (transform.position.x > 40)
{
Destroy(gameObject);
}
if (transform.position.x > dropoffLocation.x && hasCrates)
{
dropCratesCooldown--;
if (dropCratesCooldown < 0)
{
cratess = transform.GetChild(0).gameObject;
Destroy(cratess);
hasCrates = false;
}
transform.Translate(0, 0, 0);
}
else
{
transform.Translate(speed, 0, 0);
}
}
}
btw unity doesn't even show me that its an error
Thanks in advance!
Instantiate returns the instantiated game object. That's what you want to assign as a child of the truck. The way you're doing it now, you just keep assigning and reassigning crateUpgrade1's parent. That's the reason that only one of your trucks get a crate parented to it.
This is how it should be:
GameObject crate = Instantiate(crateUpgrade1);
crate.transform.parent = transform;
One other thing; the standard way to instantiate new objects is to create a prefab asset in the project, then assign a reference to it to CarScript, and then instantiate instances of that prefab. So the crateUpgrade1 = GameObject.Find("Boxes In Truck 1"); part is a little weird.
This is how it's usually done
public class CarScript : MonoBehaviour
{
// Assign this to your "crate" prefab in the Unity Inspector
[SerializeField] private GameObject cratePrefab;
private void Start()
{
if (crateUpgrade == 1)
{
GameObject crate = Instantiate(this.cratePrefab);
crate.transform.parent = transform;
}
}
}

Need to fix a problem with instantiated gameObjects

I have this problem and i don't know how to solve it,it's very confusing.I want that every time i instantiate the gameobject it gets a random color, but these clones get the same color from previous one.Like the first prefab spawn and gets random colors, now the next one spawn and olso gets random colors but the first one gets it's color,it's very confusing i know!
enter image description here
Here is the code of the gate :
public Material[] material;
public Color[] colors;
public void ColorChange()
{
colors = new[] {
Color.blue,
Color.black,
Color.red,
Color.green,
Color.yellow,
Color.white,
Color.magenta,
Color.cyan,
Color.grey };
var rnd = new System.Random();
var randomColors = colors.OrderBy(x => rnd.Next()).ToArray();
for (var i = 0; i < material.Length; i++)
{
material[i].color = randomColors[i];
}
}
private void Start()
{
ColorChange();
}
And the script of the spawner:
public GameObject Gate; // the gameobject
private float timeBtwSpawn;
public float startTimeBtwSpawn;
public float decreaseTime;
public float minTime = 0.65f;
void Start()
{
}
void Update()
{
if (timeBtwSpawn <= 0)
{
Instantiate(Gate, transform.position, Quaternion.identity);
timeBtwSpawn = startTimeBtwSpawn;
if (startTimeBtwSpawn > minTime)
{
startTimeBtwSpawn -= decreaseTime;
}
}
else
{
timeBtwSpawn -= Time.deltaTime;
}
}
Materials are shared between GameObjects, so if you change the color of a material - all objects using that material will get the new color.
Here's a few approaches (see the last one for my suggestion):
MaterialPropertyBlock
(Will not work in your case)
You could check out MaterialPropertyBlock:
Use it in situations where you want to draw multiple objects with the same material, but slightly different properties. For example, if you want to slightly change the color of each mesh drawn.
Will not work because you're re-using the materials.
Pre-created set of materials
Work around it by either creating a number of materials and chosing a random one.
(Which is what you have done, but will not work since you're reusing the materials)
New materials in runtime
Or you could create new materials in runtime.
(Should work, but next one is easier)
GameObject's Renderer.material
But I believe the easiest way should be to do:
mySpawnedGameObject.GetComponent<Renderer>().material.color = randomColor;
as opposed to using material.sharedMaterial. This should only apply to the one GameObject you choose: Renderer.material

Unity How to limit Spawner?

I am working on a game like 2 cars. So there will be two lines and I have used two object spawner which is going to spawn two shapes, i,e Circle and Square. So when player collides with circle score should update. And when Square falls player is supposed to avoid it by going to another lane. But what is the problem is something both the spawner spawns square simultaneously or with small gap. So player is not able to escape. Any solution for this. Well I guess it does not help much but here is my script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] gameobject;
public float SpawnDelay= 3f;
private GameObject objectkeeper;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
float Spawntime = SpawnDelay * Time.deltaTime; // 1 *1/60
if (Random.value < Spawntime) {
Spawn ();
}
}
void Spawn(){
int number = Random.Range (0, 2);// creating random number between 0 and 1
objectkeeper = Instantiate (gameobject [number], this.transform.position, Quaternion.identity) as GameObject;
objectkeeper.transform.parent = this.transform;
}
void OnDrawGizmos(){
Gizmos.DrawWireSphere (this.transform.position, 0.5f);
}
}
Thank you for your time and consideration
Try this,
It will only spawn one object at a time between the min-max period
It does not cleanup old objects
It allows for more than 2 prefabs
I tried keeping to your code format as much as possible
Disclaimer : I do not currently have a visual studio/mono develop open (in a boring meeting) so i have not tested this :]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] prefabs;
// Adding a min-max allows full control of spawning behaviour without editing code again.
// for a fixed time use the same value
public float MinimumSpawnDelay = 3f;
public float MaximumSpawnDelay = 6f;
private GameObject spawnedObject;
// Made this static so it retains it's value across all instances of this script.
// If you want each Instantiter object to function on it's own, remove the static keyword
private static float nextSpawnTime;
// Use this for initialization
void Start () {
// Artificial delay so we do not spawn an object directly at startup
SetNextSpawnTime();
}
// Update is called once per frame
void Update () {
if (Time.time >= nextSpawnTime) {
Spawn ();
}
}
void Spawn(){
// allows you to add more objects to the prefabs array without changing code.
var prefabToSpawn = prefabs[Ranom.Range(0, prefabs.Length)];
spawnedObject = Instantiate (prefabToSpawn, transform.position, Quaternion.identity);
spawnedObject.transform.parent = transform;
SetNextSpawnTime();
}
void OnDrawGizmos() {
Gizmos.DrawWireSphere (transform.position, 0.5f);
}
void SetNextSpawnTime(){
// a simple variable to hold when we should spawn another object, efficient.
nextSpawnTime = Time.time + Random.Range(MinimumSpawnDelay, MaximumSpawnDelay);
}
}
Try using a static variable -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Instantiter : MonoBehaviour {
public GameObject[] gameobject;
public float SpawnDelay= 3f;
private GameObject objectkeeper;
// Static variable shared across all instances of this script
public static float nextSpawn = 0f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
// check if SpawnDelay duration has passed
if (Time.time >= nextSpawn) {
// Now spawn after ramdom time
float Spawntime = SpawnDelay * Time.deltaTime; // 1 *1/60
if (Random.value < Spawntime) {
Spawn ();
}
}
}
void Spawn(){
int number = Random.Range (0, 2);// creating random number between 0 and 1
objectkeeper = Instantiate (gameobject [number], this.transform.position, Quaternion.identity) as GameObject;
objectkeeper.transform.parent = this.transform;
// Set the nextSpawn time to after SpawnDelay Duration
nextSpawn = Time.time + SpawnDelay;
}
void OnDrawGizmos(){
Gizmos.DrawWireSphere (this.transform.position, 0.5f);
}
}

How do I fix this "Does Not Contain Definition" Error?

So I'm working on a project for class in which, using Unity3D, I'm instatiating fish game objects into a game and making them appear randomly across the background. Currently, I have this code that's supposed to make my critter appear. I wrote two scripts that are supposed to make my critter game objects appear on different sides of the screen
using UnityEngine;
using System.Collections;
public class Wrangler : MonoBehaviour {
public static float halfWidth = 6;
public static float halfHeight = 5;
public GameObject critterPrefab;
public GameObject crabPrefab;
int critterCount = 0; //how many we've made so far
int critterMax = 5; //how many you want
int crabCount = 0;
int crabMax = 5;
// how long between critter spawns
void Start()
{
StartCoroutine("SpawnCritter");
StartCoroutine("SpawnCrab");
}
IEnumerator SpawnCritter()
{
while (critterCount < critterMax)
{
CreateCritter();
yield return new WaitForSeconds(1.0f);
}
}
IEnumerator SpawnCrab()
{
while (crabCount < crabMax)
{
CreateCrab();
yield return new WaitForSeconds(1.0f);
}
}
void CreateCritter() {
critterCount++;
Instantiate(critterPrefab);
}
void CreateCrab() {
crabCount++;
Instantiate(crabPrefab);
}
}
using UnityEngine; //allows us to use objects and stuff from Unity Engine
using System.Collections;
//creates a class we can manipulate called "Critter" and allows it to do the
//same thing MonoBehavior does
public class Critter : MonoBehaviour {
//variables here are for the class to use and functions can use them any time
//It looks like you can create variables first, then create the functions
protected Rigidbody rb;
// Use this for initialization
public virtual void Start () {
float startX = Random.Range (-Wrangler.halfWidth, Wrangler.halfwidth);
float startY = Random.Range (-Wrangler.halfHeight, Wrangler.halfheight);
transform.position = new Vector2 (startX, startY);
rb = GetComponent<Rigidbody> ();// Functions make classes do stuff
setVelocity ();
}
public virtual void setVelocity() {
rb.velocity = new Vector2 (-1,0);
}
// Update is called once per frame
void Update () {
if (transform.position.x > Wrangler.halfWidth)
transform.position = new Vector2 (-Wrangler.halfWidth, transform.position.y);
if(transform.position.x < Wrangler.halfWidth)
transform.position = new Vector2 (Wrangler.halfWidth, transform.position.y);
if(transform.position.y > Wrangler.halfHeight)
transform.position = new Vector2 (-Wrangler.halfHeight, transform.position.y);
if(transform.position.y < Wrangler.halfHeight)
transform.position = new Vector22 (Wrangler.halfHeight, transform.position.y);
}
}
I keep getting compiler errors in Unity that say things like "Wrangler does not contain definition for "halfWidth" when I clearly define halfWidth = 6.
What do I do to add the definition and fix these errors?
C# is case sensitive.
In Critter code you use:
float startX = Random.Range (-Wrangler.halfWidth, Wrangler.halfwidth);
float startY = Random.Range (-Wrangler.halfHeight, Wrangler.halfheight);
In Wrangler, halfWidth and halfWidth are declared with capital W and H, but here you use halfwidth and halfheight.
About the error in the image - the Wrangler as you posted is correct and should compile, however pay attention that the error is about "CrabPrefab" and not "crabPrefab" as you declared it, so maybe in your local code you used capital CrabPrefab and CritterPrefab instead?
Just pay attention to the case of your variables.

Android Controller for Unity game

I was making game in Unity 3D
and used the one click converter of unity to convert it in Android .apk
The game is opening in Android phone
but the player is not moving
Player controller Script:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
public Vector2 moving = new Vector2();
public int Bulletlimit = 0;
public int MaxBulletlimit = 3;
public bool Gun;
private float lastShotTime ;
public float fireDelay = 0.2f;
public Transform BulletDirection;
public Bullet bullet;
// Use this for initialization
void Start () {
lastShotTime = Time.time;
}
// Update is called once per frame
void Update () {
moving.x = moving.y = 0;
if (Input.GetKey ("right")) {
moving.x = 1;
} else if (Input.GetKey ("left")) {
moving.x = -1;
}
if (Input.GetKey ("up")) {
moving.y = 1;
} else if (Input.GetKey ("down")) {
moving.y = -1;
}
if (Input.GetKey ("s")) {
if(Gun){
if(Bulletlimit < MaxBulletlimit)
{
if(Time.time > lastShotTime + fireDelay)
{
Bullet clone = Instantiate (bullet, BulletDirection.position, Quaternion.identity) as Bullet;
Bulletlimit = Bulletlimit + 1;
lastShotTime = Time.time;
}
}
}
}
}
public void BulletCount()
{
Bulletlimit = Bulletlimit - 1;
}
}
How do I make him move in touch screens?
Your code is based on keystrokes - that (probably) wont apply to your touch screen.
There are a few ways you could do this, for something so simple I would probably try adding a Canvas along with some buttons to act as controls.
From there you can use the OnMouseDown()/OnMouseUp()/OnClick() methods (This also converts to touchscreens) instead of keystrokes.
How you code from there is a choice you have to make but in this case I would likely use the buttons to turn on/off movement bools and check/apply them in the Update() method.
If you're unsure of how to use the new unity UI try this...
https://unity3d.com/learn/tutorials/modules/beginner/ui/ui-canvas
If you're running the new version of unity (currently 5.1 I believe), my editor looks different to the tutorials and won't stay as default for some reason. Simply set the editor/inspector view to default if some of the options appear to be missing.
There are more complex things you can do with the actual touch inputs but I don't think you need to worry about it in this particular case.
Hope this helps :)

Categories