Unity c# gameobject's override - c#

a and objeler [] are gameobjects.I use yoketme 5 times.I get some random objects with a and give them gravity.But only 1 of the objects lose gravity after they get in bekleme.The last a is the one overwriten to all a objects that uses yoketme before timer(9 seconds).How can i make bekleme to take all gameobjects and not the last one only?
void YokEtme(){
int x = Random.Range (35, 0);
a = objeler [x];
z = a.GetComponent<Transform> ().position;
a.GetComponent<Rigidbody> ().useGravity = true;
Debug.Log (a);
StartCoroutine (bekleme ());
}
IEnumerator bekleme (){
yield return new WaitForSeconds (9);
a.GetComponent<Rigidbody> ().useGravity = false;
a.GetComponent<Rigidbody> ().constraints = RigidbodyConstraints.FreezePosition;
a.transform.position= z;
a.GetComponent<Rigidbody> ().constraints = RigidbodyConstraints.None;
Debug.Log (a);
}

Since some contexts are missing from the question, please allow me to boldly assume that:
The provided code are part of a MonoBehaviour.
a and z are fields/properties of the MonoBehaviour.
What you want to do is to randomly pick five gameobjects from objeler, apply gravity to them, and unapply gravity after a few seconds.
If my assumptions are correct, the problem is pretty simple. The reason that only one chosen object loses gravity is because that Unity allows only one coroutine being executed at a time.
That is, even though you called YokEtme five times and each will lead to StartCoroutine(bekleme()) and expected that there will be five coroutine running in parallel, actually each call to StartCoroutine(bekleme()) will discard the previously running coroutine and start a new one.
So a possible solution is to handle all the five gameobjects in one coroutine instance. Such as:
using System.Collections.Generic;
using UnityEngine;
public class SomeMono : MonoBehaviour
{
private List<GameObject> _controlledGOs = new List<GameObject>();
private List<float> _loggedZs = new List<float>();
void YokEtme()
{
int x = Random.Range(35, 0);
a = objeler [x];
a.GetComponent<Rigidbody>().useGravity = true;
_controlledGOs.Add(a);
_loggedZs.Add(a.GetComponent<Transform>().position);
}
void SetGravityForGameObjects()
{
for (var i = 0; i < 5; i++)
YokEtme();
StartCoroutine()
}
IEnumerator bekleme ()
{
yield return new WaitForSeconds (9);
for (var i = 0; i < _controlledGOs.Count; i++)
{
var a = _controlledGOs[a];
a.GetComponent<Rigidbody>().useGravity = false;
a.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezePosition;
a.transform.position= _loggedZs[i];
a.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
Debug.Log (a);
}
}
}

Related

Destroy game objects one at a time, with time interval

I have created a procedurally generated 'tiled floor' in unity3d, using a block prefab asset and script as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Wall : MonoBehaviour
{
public GameObject block;
public int width = 10;
public int height = 10;
public int timeDestroy = 1;
List<GameObject> blockList = new List<GameObject>();
void Start(){
for (int y=0; y<height; ++y)
{
for (int x=0; x<width; ++x)
{
Vector3 offset = new Vector3(x, 0, y);
GameObject hello= (GameObject)Instantiate(block, transform.position + offset, Quaternion.identity);
blockList.Add(hello);
}
}
StartCoroutine(SelfDestruct());
}
void Update()
{
SelfDestruct();
}
IEnumerator SelfDestruct()
{
yield return new WaitForSeconds(timeDestroy);
Destroy(blockList[UnityEngine.Random.Range(0,(width*height))]);
}
}
When I run the game, one of the 100 blocks has been destroyed, but then nothing happens. From my script, I was expecting one block to destroy every second, as defined by:
yield return new WaitForSeconds(timeDestroy);
where int timeDestroy = 1;
and repeat until all the blocks are gone - game over. How can I change my script so the 100 gameObjects are destroyed one after another, until none are left?
you need to put your IEnumerator SelfDestruct(); in a while loop:
IEnumerator SelfDestruct()
{
while (1<2)
{
Destroy(blockList[UnityEngine.Random.Range(0, (width * height))]);
yield return new WaitForSeconds( timeDestroy );
}
}
that way it will resume destroying the blocks, and you need to remove the SelfDestruct(); in Update.
Ps. Thanks derHugo I have forgotten that ^^.
A couple of issues here
you start a coroutine once but it doesn't really wait for anything (only at the end). There is no repetition anywhere. You probably wanted to use a loop there
you call the SelfDestruct every frame within Update which will execute everything until the first yield!
you potentially try to destroy the same object multiple times since you never remove them from your list.
Actually the entire thing can be one single coroutine!
I would also use Linq OrderBy to randomize the order of blocks once and then simply iterate and destroy them one by one in already randomized order.
Something like e.g.
using System.Linq;
...
public class Wall : MonoBehaviour
{
public GameObject block;
public int width = 10;
public int height = 10;
public int timeDestroy = 1;
// Yes this is valid and Unity will automatically
// run this as a Coroutine!
private IEnumerator Start()
{
// Don't even need this as a field only locally
// already pre-allocate the needed amount
var blockList = new List<GameObject>(height * width);
for (var y = 0; y < height; ++y)
{
for (var x = 0; x < width; ++x)
{
var offset = new Vector3(x, 0, y);
var newBlock = Instantiate(block, transform.position + offset, Quaternion.identity);
blockList.Add(new Block);
}
}
// shuffle the blocks once
var randomizedBlocks = blockList.OrderBy(blockInstance => Random.value);
// then simply iterate them in already randomized order
foreach (var blockInstance in randomizedBlocks)
{
yield return new WaitForSeconds (timeDestroy);
Destroy(blockInstance);
}
// Now you additionally also have direct control when they are all destroyed
Debug.Log("All blocks have been destroyed!");
}
}

How can I display collected amount of coins without overflow (accessing another script) in Unity?

I have two scripts. PlayerMove.cs and Spawner.cs .
In PlayerMove.cs I want a function that changes the text to the number of coins.
void CountCoin()
{
cointext.text = ": "+ coincount;
CountCoin();
}
In Spawner.cs I spawn coins and I want to increase coincount(which is public variable of PlayerMove.cs) each time I spawn.
IEnumerator StartSpawningCoins ()
{
coin.GetComponent<Renderer>().enabled=true;
yield return new WaitForSeconds(Random.Range(6f, 16f));
GameObject k = Instantiate(coin);
// my problem starts here
GameObject Player = GameObject.Find("PlayerMove");
PlayerMove playermove = Player.GetComponent<PlayerMove>();
playermove.coincount++;
//till here
float x = Random.Range(min_X,max_X);
k.transform.position = new Vector2(x, transform.position.y);
StartCoroutine(StartSpawningCoins());
}
When I use these I get stack overflow error. How can I fix it?
The problem is that in
void CountCoin()
{
cointext.text = ": "+ coincount;
CountCoin();
}
you call CountCoin() itself!
If you wanted a repeated Update you could e.g. use Invoke
void CountCoin()
{
cointext.text = ": "+ coincount;
// calls this method again after 1 second
Invoke(nameof(CountCoin), 1f);
}
or directly InvokeRepeating
void Start()
{
// calls CountCoin every second, the first time after 1 second delay
InvokeRepeating(nameof(CountCoin), 1f, 1f);
}
in order to call it every second.
Or simply remove the nested call entirely and add one to Update to make it get called every frame
void Update()
{
CountCoin();
}
Or this is what I would rather do: Make coincount a property and add the according behavior to the setter:
private int _coincount;
public int coincount
{
get { return _coincount; }
set
{
_coincount = value;
cointext.text = ": "+ value;
}
}
so everytime you change the property the code is executed automatically
Btw Find is quite expensive and should be avoided. I would change your code to
private PlayerMove playermove;
IEnumerator StartSpawningCoins ()
{
// as long as you yield inside this is ok
while(true)
{
// only do find if needed
if(!player) player = GameObject.Find("PlayerMove");
// this should probably actually be done in the prefab not via code
coin.GetComponent<Renderer>().enabled = true;
yield return new WaitForSeconds(Random.Range(6f, 16f));
GameObject k = Instantiate(coin);
playermove.coincount++;
float x = Random.Range(min_X,max_X);
k.transform.position = new Vector2(x, transform.position.y);
}
}

How do you move 500 Rigidbody cubes without slowing to 0.3 FPS?

I've been experimenting with Unity for the past few weeks; I'm still new. I have dove slightly into ECS, compute shaders, and so forth, but I really don't want to implement these solutions if it isn't necessary due to the complexity.
Can default Unity physics engine really not handle moving 500 cube instances with RigidBodies at once? Or is the way I am doing things particularly bad for performance?
Here is the code I'm using; it's just one script on an empty GameObject. It slows to 16FPS when instantiating the 500 cubes, and then slows to 0.3 FPS when moving them all at once via Rigidbody MoveTowards.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnAndMove : MonoBehaviour
{
public TargetCube TargetCube;
public GameObject CubePrefab;
public Vector3 brickPosition = new Vector3(10, 0, 0);
GameObject[] objects;
int moveCubeInstances;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Countdown());
}
IEnumerator Countdown()
{
yield return new WaitForSeconds(3f);
for (int i = 0; i < 500; i++)
{
var cubeClone = Instantiate(CubePrefab, transform.position + brickPosition, transform.rotation);
cubeClone.tag = "CubeInstance";
}
objects = GameObject.FindGameObjectsWithTag("CubeInstance");
yield return new WaitForSeconds(3f);
moveCubeInstances = 1;
while (moveCubeInstances == 1)
{
for (int i = 0; i < 500; i++)
{
objects[i].GetComponent<Rigidbody>().transform.position =
Vector3.MoveTowards(objects[i].GetComponent<Rigidbody>().transform.position, TargetCube.transform.position, 12f);
}
yield return new WaitForFixedUpdate();
}
print("exited while loop");
}
}
Thank you for any help here.
Your call to FindGameObjectsWithTag does an extra search, not only for the new 500 objects but also for all other objects on this scene
transform is already cached field and to call it you do not need to do GetComponent<>
Instantiate is a very difficult operation and it is better to do it in advance, and you can even break it into several calls per frame, for example, 50 each, but definitely not 500 in one frame, but rather use Pooling (https://docs.unity3d.com/Manual/MobileOptimizationPracticalScriptingOptimizations.html)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnAndMove : MonoBehaviour
{
public TargetCube TargetCube;
public Rigidbody CubePrefab;
public Vector3 brickPosition = new Vector3(10, 0, 0);
Rigidbody[] objects;
int moveCubeInstances;
void Start()
{
StartCoroutine(Countdown());
}
IEnumerator Countdown()
{
yield return new WaitForSeconds(3f);
objects = new Rigidbody[500];
for (int i = 0 ; i < 500 ; i++)
{
Rigidbody cubeClone = Instantiate(CubePrefab, transform.position + brickPosition, transform.rotation);
cubeClone.tag = "CubeInstance";
objects[i] = cubeClone;
if (i % 50 == 0)
{
yield return new WaitForFixedUpdate();
}
}
yield return new WaitForSeconds(3f);
moveCubeInstances = 1;
while (moveCubeInstances == 1)
{
for (int i = 0 ; i < 500 ; i++)
{
objects[i].transform.position =
Vector3.MoveTowards(objects[i].transform.position, TargetCube.transform.position, 12f);
}
yield return new WaitForFixedUpdate();
}
print("exited while loop");
}
}

How can I pause spawning in a coroutine until all the spawned objects are destroyed?

I just don't know how to write the if part. The rest is just instantiate position and quaternion stuff.... Probably.
I want to know if the GameObject rocketspawnblue and GameObject rocketspawnred are gone, so I can spawn the next pair until all the pairs in the loop are spawned.
This is my spawning coroutine I put in Start(). however that's probably won't work because it needs to be check every frame until i take all my rockets....
for (int i = 1; i < 6; i++)
{
yield return new WaitForSeconds(1);
GameObject rocketspawnblue = Instantiate(
rocketblue,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor1 = rocketspawnblue.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0,colors.Length)];
rocketscolor1.color = Color.blue;
GameObject rocketspawnred = Instantiate(
rocketred,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor2 = rocketspawnred.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0, colors.Length)];
rocketscolor2.color = Color.red;
}
Use a List as a field to track the rockets:
private List<GameObject> rockets;
At the beginning of Start(), instantiate the list:
rockets = new List<GameObject>();
In your coroutine, clear the list with Clear then use Add to add rockets to your list. After you're done adding, use WaitWhile to loop until there are no rockets left. We'll write a method called AnyRocketsLeft that returns true if there are any that are not yet destroyed.
After the WaitWhile, use WaitForSeconds to wait for a second to give time between batches.
for (int i = 1; i < 6; i++)
{
rockets.Clear();
GameObject rocketspawnblue = Instantiate(
rocketblue,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor1 = rocketspawnblue.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0,colors.Length)];
rocketscolor1.color = Color.blue;
rockets.Add(rocketspawnblue);
GameObject rocketspawnred = Instantiate(
rocketred,
new Vector3(Random.Range(-15, 15), (Random.Range(-15, 15))),
Quaternion.identity);
SpriteRenderer rocketscolor2 = rocketspawnred.GetComponent<SpriteRenderer>();
//rocketscolor.color = colors[Random.Range(0, colors.Length)];
rocketscolor2.color = Color.red;
rockets.Add(rocketspawnred);
yield return new WaitWhile(() => AnyRocketsLeft());
yield return new WaitForSeconds(1);
}
// ran out of rockets
// set up the "next level" prompt, etc. here
To check if any rockets are not yet destroyed, we can check their activeInHierarchy property. Just loop through the list of rockets and return true if you find any, and false otherwise.
private bool AnyRocketsLeft() {
foreach (int i = 0 ; i < rockets.count ; i++) {
if (rockets[i].activeInHierarchy) return true;
}
return false;
}
If you're using Linq, you can use List.Any():
private bool AnyRocketsLeft() {
return rockets.Any(o => o.activeInHierarchy);
}

Need to decrease spawn wait time interval(float) based on time.time value , Instantiation of two objects should decrease by some proportion

I am writing a small game where objects arrives randomly , I need to implement a logic where I can calculate wait interval for next instantiation as per timer.(As game progresses spawn wait time should decrease).
i am trying to spawn objects in the game those object are having lifetime until next appear , player need to tab fingers as they appear and i am trying to instantiate these fingers faster as game progress
only struggling to get mathematical equation to get value for spawnWait based Time.time . i hope i have written it properly now.
i need equation for graph something like in first Quater(+x,+y) for time.time & spawn wait. although not very much sure.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameController : MonoBehaviour {
// Use this for initialization
public GameObject GoodThumb;
public GameObject BadThumb;
public int max = 22;
public float spanWait = 10.0f;
public static bool gameOver = false;
Vector3 theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(0,0,11));
Quaternion theNewRotation= Quaternion.Euler(0,0,0);
void Start () {
StartCoroutine(spawn());
OnGameStart();
}
void Update () {
Time.deltaTime
}
// Update is called once per frame
void Update () {
// SpawnRandom ();
ChooseFinger ();
}
public int ChooseFinger()
{
return Random.Range (1, 5);
}
void OnGameStart()
{
//Debug.Log ("Game Started");
}
IEnumerator spawn() {
GameObject go;
while(true)
{
int randomLoop = Random.Range(2,5);
for (int i = 0; i < randomLoop; i++)
{
pickRandomFinger();
go = Instantiate(GoodThumb);
go.transform.position = theNewPos;
go.transform.rotation = theNewRotation;
// Debug.Log(theNewPos);
// Debug.Log(theNewRotation);
yield return new WaitForSeconds(spanWait);
}
pickRandomFinger();
go = Instantiate(BadThumb);
go.transform.position = theNewPos;
go.transform.rotation = theNewRotation;
yield return new WaitForSeconds(spanWait);
if (gameOver)
{
break;
}
}
}
public void pickRandomFinger()
{
int FingerNo = ChooseFinger();
Debug.Log (FingerNo);
switch (FingerNo)
{
case 1: // finger1 type bottom good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(Random.Range(0.0F,1.0F),1.0F, 11));
theNewRotation = Quaternion.Euler(0,0,180);
break;
case 2: // finger4 type right good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(1.0F,Random.Range(0.0F,1.0F), 11));
theNewRotation = Quaternion.Euler(0,0,90);
break;
case 3: // finger2 type top good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(Random.Range(0.0F,1.0F),0.0F, 11));
theNewRotation = Quaternion.Euler(0,0,0);
break;
case 4: // finger3 type left good
theNewPos = Camera.main.ViewportToWorldPoint(new Vector3(0.0F,Random.Range(0.0F,1.0F),11));
theNewRotation = Quaternion.Euler(0,0,270);
break;
}
}
}
This questions is incredibly vague,
From what I can gather, you want there to be some sort of inverse correlation between the amount of time the user has been in the game and the time between spawns.
There are a million ways to do this, and the correct answer will be completely dependent on the kind of behavior you want the game to exhibit.
timeBetweenSpawns = minTime + max(defaultTime - (timeInGame*constant),0)
This ( very simple and generalized ) code will give you a very simple inverse relationship between time spent in game and the time between items spawning.
You're going to have to think of your own solution and find what works best for your project.

Categories

Resources