I have created a script who will spawn objects and give tag and color for one element of prefab. But script not working with clones. This 2 scripts, GameManager( he must spawn objects) and
RandomTagAndColor( he give to element of prefab tag and name). And in scene of game, where objects is spawning, script give tag and colour only to first prefab. In game those prefabs 10. Well, I'm sorry if question is stupid, this first thing, what i doing without books,guides.
GameManager script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class GameManager : MonoBehaviour
{
// Start is called before the first frame update
public GameObject firstBarrier;
public GameObject secondBarrier;
public int numOfBarriers = 0;
System.Random rnd = new System.Random();
System.Random rndY = new System.Random();
Vector3 vector = new Vector3(5, 1/3 , 1);
void Start()
{
for (numOfBarriers = 0; numOfBarriers < 10; numOfBarriers++)
{
int ewq = rnd.Next(1, 20);
int rY = rndY.Next(1, 4);
if(ewq <= 10)
{
Instantiate(firstBarrier, vector, Quaternion.identity);
}
else
{
Instantiate(secondBarrier, vector, Quaternion.identity);
}
vector.x -= 7;
vector.y = rY;
}
}
void Update()
{
}}
RandomColorAndTag script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class RandomColorAndTag : MonoBehaviour
{
GameObject Bar;
System.Random randomElement = new System.Random();
int el = 0 ;
void Start()
{
el = randomElement.Next(2, 6);
GameObject.Find($"Cube ({el})").GetComponent<Renderer>().material.color = Color.green;
if (GameObject.Find($"Cube ({el})").GetComponent<Renderer>().material.color == Color.green)
{
GameObject.Find($"Cube ({el})").transform.tag = "Green";
}
}
void Update()
{
}
}
As mentioned there are lot of unclear issues in your codes.
I will assume the RandomColorAndTag is attached to the objects you spawn. In that case don't use Find but rather simply assign the random color and tag to yourself
what is a random value between 1 and 19 good for, of all you ever do with it is check whether it is bigger or smaller than 10? => Simply use only a random 0 or 1 and check whether it is 0 ;)
I'm unsure exactly what items should be green now .. I assume among the 10 spawned items you want to pick one and make it special..
finally I wouldn't even let the objects assign their own tag and color but let the GameManager trigger it
So I would do something like
public class GameManager : MonoBehaviour
{
public TagAndColorController fortBarrier;
public TagAndColorController secondBarrier;
public int numOfBarriers = 10;
public Vector3 vector = new Vector3(5, 1f/3f , 1);
public Color specialColor = Color.Green;
public string specialTag = "Green";
private void Start()
{
var specialIndex = Random.Range(0, numberOfBarriers);
for (var i = 0; i < numOfBarriers; i++)
{
// upper bound is exclusive -> this returns 0 or 1
var rndBarrier = Random.Range(0, 2);
// not sure if intended but again: upper bound is exclusive -> this return 1, 2, or 3
var rY = Random.Range(1, 4);
var tagAndColor = Instantiate(rndBarrier == 0 ? firstBarrier : secondBarrier, vector, Quaternion.identity);
if(i == numberOfBarriers)
{
tagAndColor.SetColorAndTag(specialColor, specialTag);
}
vector.x -= 7;
vector.y = rY;
}
}
}
And the other script has no logic whatsoever but rather only is a quick access to the objects renderer and bundles the behavior
public class TagAndColorController : MonoBehaviour
{
[SerializeField] private Renderer _renderer;
public void SetColorAndTag(Color newColor, string newTag)
{
gameObject.tag = newTag;
if(!_renderer) _renderer = GetComponent<Renderer>();
_renderer.material.color = newColor;
}
}
Related
I am trying to create a game in Unity. I need to spawn a sphere every time the player has eliminated the other sphere.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomGeneration : MonoBehaviour
{
public GameObject Sphere;
public int xPos;
public int yPos;
public int zPos;
public int sphereCount;
// Start is called before the first frame update
void Start()
{
StartCoroutine(SphereDrop());
//sphereCount = GameObject.FindGameObjectsWithTag("Sphere").Length - 1;
}
// Update is called once per frame
void Update()
{
sphereCount = GameObject.FindGameObjectsWithTag("Sphere").Length - 1;
print(sphereCount);
//print(GameObject.FindGameObjectsWithTag("Sphere").Length - 1);
}
IEnumerator SphereDrop()
{
while(sphereCount == 0)
{
xPos = Random.Range(-4, 4);
yPos = Random.Range(1, 3);
zPos = Random.Range(-4, 4);
Instantiate(Sphere, new Vector3(xPos, yPos, zPos), Quaternion.identity);
yield return new WaitForSeconds(0.1f);
sphereCount += 1;
}
}
}
The sphereCount correctly measures how many spheres are in the game, but new spheres aren't being created when sphereCount is 0.
The problem is that the first time you enter the while loop, your sphereCount is 1, so it won't enter again, and consequently will never call the instantiate again.
Anyway I won't do it like this, instead I'll have two scripts, SphereSpawner, in charge of the instatiation control, and SphereObject, in charge of SphereBehaviours.
Add an event on the OnDestroy callback of the Sphere so it will call SphereSpawner to spawn a new one.
Your spawner will look like:
public class SphereSpawner : MonoBehaviour
{
public GameObject spherePrefab;
public void InstantiateNewSphere()
{
float xPos = Random.Range(-4, 4);
float yPos = Random.Range(1, 3);
float zPos = Random.Range(-4, 4);
GameObject newSphere = Instantiate(spherePrefab, new Vector3(xPos, yPos, zPos), Quaternion.identity);
newSphere.GetComponent<SphereObject>().spawnerReference = this;
}
}
and your SphereObject like this:
public class SphereObject : MonoBehaviour
{
public SphereSpawner spawnerReference = null;
private void OnDestroy()
{
spawnerReference.InstantiateNewSphere();
}
}
But ideally you will have a Pool of spheres.
Answer moved from body of question:
I managed to fix the issue by adding StartCoroutine(SphereDrop()); inside void Update() and removing it where it currently is.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public int delay = 3;
public int stairsNumber = 5;
public int stairsHeight = 0;
public Vector3 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start ()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update ()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsSize.x; i++)
{
for (float k = 0; k <= stairsSize.y; k++)
{
stairsPosition = new Vector3(i, stairsHeight, k);
GameObject stairs = Instantiate(stairsPrefab, stairsPosition, Quaternion.identity);
stairs.transform.localScale = new Vector3(stairsSize.x, 1 , stairsSize.y);
stairsHeight += 1;
yield return new WaitForSeconds(delay);
}
}
}
private void CalculateNextStair()
{
}
}
I messed it up. For example I want to build 5 stairs but the loops are over the stairs size and not number of stairs.
Second it's creating 10 sets of stairs not 5 stairs:
Another problem is how can I make that each stair will be build slowly ? Now it's just Instantiate slowly with delay but how can I generate each stair with delay?
Screenshot of the script inspector:
My current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public float delay = 0.3f;
public int stairsNumber = 5;
public int stairsPositions = 0;
public int stairsStartPositionHeight = 0;
public float stairsScalingHaight = 1;
public Vector2 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsNumber; i++)
{
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsPositions, stairsPositions);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
stairsScalingHaight,
stairsSize.y);
stairsStartPositionHeight += 1;
yield return new WaitForSeconds(delay);
}
}
private void CalculateNextStair()
{
}
}
There's no reason to loop over the size of the stairs at all; you want to loop over stairsNumber, which is yet unused in your code.
Also, you don't need to change the x component of your stairs' positions. Keep it at 0f (or whatever you need).
The y and z components of your stairs positions (relative to the starting point) should both be factors of stairHeight. In this particular case, you want them to be equal to stairHeight, so that you get "square" step shapes.
private IEnumerator BuildStairs()
{
for (int i = 0; i <= stairsNumber ; i++) {
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsHeight, stairsHeight);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
1f ,
stairsSize.y);
stairsHeight += 1f;
yield return new WaitForSeconds(delay);
}
}
If you change stairSize to be a Vector3, then you can just use stairSize directly as the localScale, and increment stairsHeight by stairsSize.y instead of just 1f.
If you want to offset the starting position of your stairs, you need to include an offset. I recommend keeping it separate from the height counter until you need to add them.
Also, if you want to have rectangular sized steps, keep a widthFactor to multiply by the height to find how far each step moves horizontally.
Combining these changes might look like this:
Vector3 stairSize;
float stepWidthFactor=1f;
Vector3 stairsStartPosition;
private IEnumerator BuildStairs() {
for (int i = 0; i <= stairsNumber ; i++) {
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + stairsHeight,
stairsStartPosition.z + stairsHeight*stepWidthFactor);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairsHeight += stairsSize.y;
stairs.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}
The code below works Except for the Instancated objects moving on their own. I want my Instancated objects to move back and fourth between pointA and pointB at a speed 0.5f.
Note: Im not trying to use the commented code in Start() and Update() because this file is attached to the camera.
With current code: My objectList objects are moving as expected just not their instantiated objects. I would like the Instantiated objects to move like ping-pong with their off-set
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectsSpawner : MonoBehaviour {
public GameObject[] objectList;
public GameObject[] objectSpawns;
int val;
public float speed = 0.5f;
Vector3 pointA;
Vector3 pointB;
// Use this for initialization
void Start () {
val = PlayerPrefs.GetInt("CannonPowerVal");
addToList();
for (int i = 1; i < objectSpawns.Length; i++){
pointA = new Vector3(-3.8f, objectSpawns[i].transform.localPosition.y, 0);
pointB = new Vector3(3.8f, objectSpawns[i].transform.localPosition.y, 0);
}
//pointA = new Vector3(-3.8f, transform.localPosition.y, 0);
//pointB = new Vector3(3.8f, transform.localPosition.y, 0);
}
// Update is called once per frame
void Update()
{
//PingPong between 0 and 1
float time = Mathf.PingPong(Time.time * speed, 1);
//transform.position = Vector3.Lerp(pointA, pointB, time);
for (int i = 1; i < objectSpawns.Length; i++)
{
objectSpawns[i].transform.position = Vector3.Lerp(pointA, pointB, time);
}
}
public void addToList(){
objectSpawns = new GameObject[val];
int max = objectList.Length;
int counter = 8; // set first object out of screen sight
// Adds Scene Objects from objectList to objectSpawns
// Size of SceneObjects determined by CannonPowerVal
for (int i = 0; i < PlayerPrefs.GetInt("CannonPowerVal"); i++){
objectSpawns.SetValue(objectList[Random.Range(0, max)], i);
// Random x spawn(-2.8f, 2.8f)
Instantiate(objectSpawns[i], new Vector2(transform.localPosition.x + Random.Range(-2.8f,2.8f), transform.localPosition.y + counter), Quaternion.identity);
counter = counter + 5;
}
}
}
after instantiating the object, you forgot to add the reference to the list of objectSpawns.
In addToList() method do:
GameObject object = Instantiate(objectSpawns[i],...);
objectSpawns.Add(object)
I have a SceneController that's supposed to initialize a set of empty GameObject spawners, each working together at the same rhythm. The RockSpawners receive an array of time delays and wait the X seconds between spawning another rock.
I set the _nextSpawn = float.maxValue when the spawners start and plan to overwrite this after "Initializing" them (my own method), however even though my debug logs say I've overwritten my _nextSpawn value while initializing, the update loop is still reporting float.maxValue and nothing ends up spawning because _timeSinceLastSpawn hasn't exceeded float.maxValue seconds.
Is there something I'm missing with the scope of my _nextSpawn variables? It doesn't seem to be a "this" vs "local" issue, at least at first glance.
Debug output: 0 0 3 3. 0's stay the same, 3's will vary based on rng.
SceneController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SceneController : MonoBehaviour {
[SerializeField] private GameObject _rockSpawnerPrefab;
public int numRocks = 6;
public int minSpawnDelaySec = 1;
public int maxSpawnDelaySec = 3;
private bool spawnersInitialized = false;
void Start () {
InitializeSpawners();
}
void Update () {
}
void InitializeSpawners() {
float[] pattern = new float[numRocks];
for (int i = 0; i < numRocks; i++) {
// Generate delays at half second increments within bounds
float delay = Mathf.Floor(Random.value * ((float)(maxSpawnDelaySec + 0.5f - minSpawnDelaySec) / 0.5f));
delay = delay * 0.5f + minSpawnDelaySec;
pattern[i] = delay;
}
GameObject spawner = Instantiate(_rockSpawnerPrefab) as GameObject;
spawner.transform.position = new Vector3(0, 4, 0);
RockSpawner rockSpawnerScript = spawner.GetComponent<RockSpawner>();
rockSpawnerScript.Initialize(pattern);
}
}
RockSpawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RockSpawner : MonoBehaviour {
[SerializeField] private GameObject _rockPrefab;
public float minSpawnDelay = 3f;
public float maxSpawnDelay = 6f;
private float[] _pattern;
private int _currentPattern;
private float _timeSinceLastSpawn;
private float _nextSpawn;
void Start () {
_currentPattern = -1;
_nextSpawn = float.MaxValue;
}
void Update () {
if (_pattern == null) return;
_timeSinceLastSpawn += Time.deltaTime;
if (_timeSinceLastSpawn > _nextSpawn) {
GameObject rock = Instantiate(_rockPrefab) as GameObject;
rock.transform.position = transform.position;
NextTimer();
}
}
public void Initialize(float[] pattern) {
_pattern = pattern;
NextTimer();
}
private void NextTimer() {
_timeSinceLastSpawn = 0;
_currentPattern += 1;
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
this._nextSpawn = _pattern[_currentPattern];
Debug.Log(_nextSpawn);
Debug.Log(this._nextSpawn);
}
}
It's not about scoping, it's about call order. When you create a GameObject its Start method is called on the frame it's enabled, not when the object is created. So your code will call Initialize first, then Start which overwrites the values.
Remove the code in Start and handle everything in Initialize and it should work as you want.
I'm trying to get variable from an empty gameObject's script, but I can't assign that gameObject on the inspector. These are the screen shots and codes from my game.
Well, I have this code to load when the game is starting. Land and Prince are objects that made from this code.
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class loadGame : MonoBehaviour
{
public static loadGame loadSave;
public GameObject objPrince;
public Pangeran charPrince;
public Transform prefPrince;
public Sprite[] spriteTanah;
public Dictionary<string, Tanah> myTanah = new Dictionary<string, Tanah>();
public Dictionary<string, GameObject>objTanah = new Dictionary<string, GameObject>();
public Tanah tempTanah;
public GameObject tempObjTanah;
public Transform prefTanah;
public float mapX;
public float mapY;
public int i = 0;
public int j = 0;
public int rows = 9;
public int column = 9;
void Awake(){
if(loadSave == null)
{
DontDestroyOnLoad(gameObject);
loadSave = this;
}
else if(loadSave != this)
Destroy(gameObject);
}
// Use this for initialization
void Start ()
{
Load ();
}
// Update is called once per frame
void Update ()
{
}
public void Load()
{
if(File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
{
}
else
{
charPrince = new Pangeran ("Prince", "04Okt1993", 0, 0, 0, 0, 0, false, false);
objPrince = GameObject.Instantiate (prefPrince, new Vector3 (0, 0, 0), Quaternion.identity) as GameObject;
//objPrince.name = "Prince";
charPrince.locationY = 0f;
charPrince.locationX = 0f;
charPrince.hadapAtas = false;
charPrince.hadapKanan = true;
charPrince.stamina = 100f;
charPrince.exp = 0f;
charPrince.speed = 0f;
for(i = 0 ; i < rows ; i ++)
{
for(j = 0; j<column ; j++)
{
mapX = (i-j) * 0.8f;
mapY = (i+j) * 0.4f;
if(i>=1 && j>=1 && i<=5 && j<=5)
{
prefTanah.name = "land-"+j.ToString("0#")+"-"+i.ToString("0#");
tempTanah = new Tanah("land-"+j.ToString("0#")+"-"+i.ToString("0#"),mapX,mapY,"land",spriteTanah[0],spriteTanah[1],spriteTanah[2]);
myTanah.Add("land-"+j.ToString("0#")+"-"+i.ToString("0#"),tempTanah);
tempObjTanah = GameObject.Instantiate(prefTanah, new Vector3(mapX,mapY,0),Quaternion.identity)as GameObject;
objTanah.Add("land-"+j.ToString("0#")+"-"+i.ToString("0#"),tempObjTanah);
}
else
{
prefTanah.name = "snow-"+j.ToString("0#")+"-"+i.ToString("0#");
tempTanah = new Tanah("snow-"+j.ToString("0#")+"-"+i.ToString("0#"),mapX,mapY,"snow");
myTanah.Add("snow-"+j.ToString("0#")+"-"+i.ToString("0#"),tempTanah);
tempObjTanah = GameObject.Instantiate(prefTanah, new Vector3(mapX,mapY,0),Quaternion.identity)as GameObject;
objTanah.Add("snow-"+j.ToString("0#")+"-"+i.ToString("0#"),tempObjTanah);
}
}
}
}
}
}
I'm trying to access one of some variables from code above, but I can't assign it in the inspector.
but I can't do it.
please help me. Thank you.
The problem is that the loadLand variable is of type LoadGame which is a script. What you are trying to do is to add a GameObject to this variable. So change the public variable type to
public GameObject LoadLandObject;
private LoadGame loadLand;
and create a private LoadGame variable which is the reference to your script.
Add in the Start() method
loadLand = (LoadGame)LoadLandObject.GetComponent<LoadGame>();
With this you load the script LoadGame of the GameObject into the variable.
Do you ever set your plantingScript.loadLand to the instance of loadGame.loadSave? This must be done after Awake in your case to be sure the instance has been set.
Can I ask, what are you trying to do?
You should simply just assign you loadGame script in the inspector of plantingScript and not use statics at all. They will bite your ass sooner or later (and I'm guessing it already is).
Have a similar problem. Did n't find simple and clear solution. May be what i did help to you.
Create empty game object.
Attach loadGame.cs to it (if you whant to control when script starts - uncheck it,in this case don't foget to set loadLand.enabled to true in plantingScript when needed)
Drag this object to you loadLand field
Sample project