How can I generate the units at the same time? - c#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
[Header("Stairs Prefb")]
public GameObject stairsPrefab;
[Space(5)]
[Header("Platforms")]
public bool addPlatforms = false;
public GameObject platformsPrefab;
[Space(5)]
[Header("Settings")]
[Range(1, 20)]
public int numberOfUnits = 1;
public float delay = 3;
public int stairsNumber = 5;
public Vector3 stairsStartPosition;
public Vector3 stairSize;
public Vector3 stairsSize;
public float stepWidthFactor = 1f;
private Vector3 stairsPosition;
private GameObject stairsParent;
private int oldNumberOfUnits = 0;
// Use this for initialization
void Start()
{
oldNumberOfUnits = numberOfUnits;
for (int i = 0; i < numberOfUnits; i++)
{
stairsParent = new GameObject();
stairsParent.name = "Stairs";
StartCoroutine(BuildStairs());
}
}
// Update is called once per frame
void Update()
{
if(oldNumberOfUnits != numberOfUnits)
{
StartCoroutine(BuildStairs());
oldNumberOfUnits = numberOfUnits;
}
}
private IEnumerator BuildStairs()
{
for (int i = 1; i <= stairsNumber; i++)
{
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + (i * stairsSize.y),
stairsStartPosition.z + (i * stairsSize.y) * stepWidthFactor);
GameObject stair = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stair.tag = "Stair";
stair.transform.parent = stairsParent.transform;
stair.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
stairsParent.AddComponent<MoveObjects>().Init();
}
}
In the Start I'm doing a loop and start the Coroutine according to the numberOfunits.
It's working fine if numberOfUnits is 1. But is it's more then 1 for example 2 it's first creating the first set of stairs but then on the second "Stairs" parent it's creating only 1 stair. I don't want it to wait to finish the first Coroutine I want in the same time to create number of Coroutine's of stairs.
And I want also to add a gap between each stairs unit.
And also to make that in the Update if I change the numberOfUnits it will add/destroy more stairs units. All the stairs units should be Instantiate inside StartCoroutine.

You are mistaking how the coroutine works its not at thread. What is happening is your continually invoking the coroutine so its starting over and over again not creating a separate instance.
what you should do is create create a prefab and Instantiate that to do the work. My last remark was about threads but you wont be able to instantiate anything unless its on the main thread so the easiest way to get this done would be like so.
public GameObject yourGoWithAboveClassOnIt;
void Start()
{
oldNumberOfUnits = numberOfUnits;
for (int i = 0; i < numberOfUnits; i++)
{
Instantiate(yourGoWithAboveClassOnIt);
}
}
your prior class will remove this
void Start()
{
//oldNumberOfUnits = numberOfUnits;
//for (int i = 0; i < numberOfUnits; i++)
//{
stairsParent = new GameObject();
stairsParent.name = "Stairs";
StartCoroutine(BuildStairs());
//}
}

Related

I have 3 scripts one duplicate a script and the third should get created objects but I can't get this objects. How can I get the objects?

I have 3 scripts one duplicate a script and the third should get created objects but I can't get this objects. How can I get the objects?
The first script is Generate Stairs Units. This script is attached to a empty GameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairsUnits : MonoBehaviour
{
[Header("Stairs Units Prefab")]
public GameObject stairsUnitsPrefab;
[Space(5)]
[Header("Settings")]
[Range(1, 20)]
public int numberOfUnits = 1;
public static GameObject Unit;
private int oldNumberOfUnits = 0;
private List<GameObject> units = new List<GameObject>();
// Use this for initialization
void Start ()
{
oldNumberOfUnits = numberOfUnits;
var unitsParent = GameObject.Find("Stairs Units");
for (int i = 0; i < numberOfUnits; i++)
{
Unit = Instantiate(stairsUnitsPrefab, unitsParent.transform);
Unit.name = "Stairs " + i.ToString();
units.Add(Unit);
Unit.AddComponent<MoveObjects>();
}
}
// Update is called once per frame
void Update ()
{
}
}
The GenerateStairsUnits script duplicate a prefab of the second script Generate Stairs:
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
[Header("Stairs Prefb")]
public GameObject stairsPrefab;
[Space(5)]
[Header("Platforms")]
public bool addPlatforms = false;
public GameObject platformsPrefab;
[Space(5)]
[Header("Settings")]
public float delay = 3;
public int stairsNumber = 5;
public Vector3 stairsStartPosition;
public Vector3 stairSize;
public Vector3 stairsSize;
public float stepWidthFactor = 1f;
public GameObject moveobjects;
private Vector3 stairsPosition;
private GameObject stairsParent;
// Use this for initialization
void Start()
{
moveobjects = GameObject.Find("Move Objects");
stairsParent = new GameObject();
stairsParent.name = "Stairs";
stairsParent.transform.parent = GenerateStairsUnits.Unit.transform;
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (int i = 1; i <= stairsNumber; i++)
{
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + (i * stairsSize.y),
stairsStartPosition.z + (i * stairsSize.y) * stepWidthFactor);
GameObject stair = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stair.tag = "Stair";
stair.transform.parent = transform;
stair.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}
}
The GenerateStairsUnits also add as component the third script Move Objects:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class MoveObjects : MonoBehaviour
{
public List<GameObject> objectsToMove = new List<GameObject>();
public AnimationCurve curve;
public float stepsPerSecond = 1f;
public bool changeDirection = false;
private Vector3 trackStart;
private Vector3 trackEnd;
private Vector3 horizontalTravel;
private float verticalTravel;
private float divisor;
private float phase = 0f;
// Use this for initialization
public void Init()
{
if (curve == null)
{
curve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1, 1));
}
curve.preWrapMode = WrapMode.Clamp;
curve.postWrapMode = WrapMode.Clamp;
trackStart = objectsToMove[0].transform.position;
int count = objectsToMove.Count;
var span = objectsToMove[count - 1].transform.position - trackStart;
divisor = 1f / count;
horizontalTravel = (count + 1) * span * divisor;
horizontalTravel.y = 0f;
verticalTravel = span.y;
trackEnd = trackStart + (count + 1) * span / count;
}
// Update is called once per frame
void Update()
{
if (objectsToMove != null && objectsToMove.Count > 0 && curve != null)
{
AnimationCurve();
}
}
private void AnimationCurve()
{
phase = Mathf.Repeat(phase + stepsPerSecond * divisor * Time.deltaTime, 1f);
for (int i = 0; i < objectsToMove.Count; i++)
{
float t = Mathf.Repeat(phase + i * divisor, 1f);
// Get the height of the curve at this step.
float curveHeight = curve.Evaluate(t) * verticalTravel;
if (changeDirection)
{
objectsToMove[i].transform.position = trackStart // First step
- horizontalTravel * t // evenly spaced horizontal
+ curveHeight * Vector3.up; // curving vertical
}
else
{
objectsToMove[i].transform.position = trackStart // First step
+ horizontalTravel * t // evenly spaced horizontal
+ curveHeight * Vector3.up; // curving vertical
}
}
}
private void StraightLineTrack()
{
float divisor = 1f / objectsToMove.Count;
// Compute the current phase of the escalator,
// from 0 (1st step at track start) to 1 (1st step at track end)
phase = Mathf.Repeat(phase + stepsPerSecond * divisor * Time.deltaTime, 1f);
// Place each step a proportional distance along the track.
for (int i = 0; i < objectsToMove.Count; i++)
{
float t = Mathf.Repeat(phase + i * divisor, 1f);
objectsToMove[i].transform.position = Vector3.Lerp(trackStart, trackEnd, t);
}
}
}
Now the problem:
After duplicating the GenerateStairs and creating stairs for each Stairs Unit and adding the Move Objects component the result is:
In the Hierarchy I have empty GameObject name Stairs Units. Under Stairs Units there are the Units Stairs 0, Stairs 1, Stairs 2, Stairs 3, Stairs 4. And under each Stairs there are the Stairs.
Each Unit for example Stair 0 have also attached the Move Objects script.
Now my problem is how to get the stairs of each unit to the Move Objects objectsToMove List.
In the Move Objects I have a List name objectsToMove. For example under Stairs 0 there are 10 stairs I need to get this 10 stairs to the objectsToMove of Stairs 0. Then the next 10 stairs of the Stairs 1 and so on. But I can't figure out how to add the stairs of each unit to the objectsToMove.
In the end the objectsToMove that what should move the stairs of each Stairs Unit.
Since your Stairs N gameobject attaches two scripts GenerateStairs and MoveObjects on it, you can get the MoveObjects's reference by calling GetComponent before generating the Stairs and pass it to the BuildStairs function.
void Start()
{
...
MoveObjects moveObjects = gameObject.GetComponent<MoveObjects>();
StartCoroutine(BuildStairs(moveObjects));
}
By getting the MoveObjects's reference, you can then pass this reference into your BuildStairs function and add those generating stairs into list objectsToMove inside MoveObjects.
Modify the function and pass MoveObjects like below:
private IEnumerator BuildStairs(MoveObjects moveObjects)
{
...
moveObjects.objectsToMove.add (stair);
...
}
About GetComponent.

How can I move item from end of list to the start?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveObjects : MonoBehaviour
{
public float speed = 3f;
private GameObject[] objectstoMove;
private List<GameObject> objectsMoving = new List<GameObject>();
private float distanceTravelled = 0;
private Vector3 lastPosition;
// Use this for initialization
public void Init()
{
objectstoMove = GameObject.FindGameObjectsWithTag("Test");
objectsMoving = new List<GameObject>(objectstoMove);
lastPosition = objectstoMove[objectstoMove.Length].transform.position;
}
// Update is called once per frame
void Update()
{
if (objectstoMove != null)
{
float step = speed * Time.deltaTime;
for (int i = 0; i < objectstoMove.Length; i++)
{
if(distanceTravelled >= 50.0f)
{
objectsMoving.Remove(objectsMoving[objectsMoving.Count]);
}
objectsMoving[i].transform.Translate((objectsMoving[i].transform.up + objectsMoving[i].transform.forward) * step);
distanceTravelled += Vector3.Distance(objectsMoving[objectsMoving.Count].transform.position, lastPosition);
lastPosition = objectsMoving[objectsMoving.Count].transform.position;
}
}
}
}
In this part I want to take the last object in the list and move it to the start of the list:
if(distanceTravelled >= 50.0f)
{
objectsMoving.Remove(objectsMoving[objectsMoving.Count]);
}
The idea in general is to move the last item object from the list to the start of the list and keep moving the objects all the time but each time the last object in the list is distanceTravelled >= 50.0f move it to the start of the list. Same idea as cyclic if I'm not wrong.
Do something like this:
if(distanceTravelled >= 50.0f)
{
var moveToFirst = objectsMoving.Last();
objectsMoving.RemoveAt(objectsMoving.Count-1);
objectsMoving.Insert(0, moveToFirst);
}

Using Coroutine in Unity

Am working on Unity and here is what I want to do: play the animationType in a time difference of 10sec. I want the code to loop through the animations and play them each for 10seconds. The code runs without errors, except the result is not what I expected it to be. It plays the first animation,Boxing, for 10 seconds and just when it's about to play the Backflip animation, it starts to do some weird thing to the character. That's where is goes wrong.
Here is my code:
public class BeBot_Controller : MonoBehaviour
{
Animator anim;
string animationType;
string[] split;
int arrayLength;
void Start()
{
//AndroidJavaClass pluginClass = new AndroidJavaClass("yenettaapp.my_bebot_plugin.My_BeBot_Plugin");
//animationType = pluginClass.CallStatic<string>("getMessage");
animationType="Null,Boxing,Backflip";
split = animationType.Split(',');
anim = gameObject.GetComponentInChildren<Animator> ();
arrayLength = split.Length;
}
// Update is called once per frame
void Update () {
if (arrayLength > 1){
StartCoroutine ("LoopThroughAnimation");
}
}
IEnumerator LoopThroughAnimation()
{
for (int i = 1 ; i < arrayLength; i++) {
animationType = split [i];
//anim.SetInteger ("AnimPar", 0);
anim.Play (animationType);
yield return new WaitForSeconds (10);
}
}
}
So what did I do wrong here ? Is there any other way I can solve this problem ?
Since your animation loop only needs to be called once, simply move StartCoroutine() to Start() and remove the Update() stuff:
public class BeBot_Controller : MonoBehaviour
{
private Animator anim;
private string animationType;
private string[] split;
private int arrayLength;
void Start ()
{
//AndroidJavaClass pluginClass = new AndroidJavaClass("yenettaapp.my_bebot_plugin.My_BeBot_Plugin");
//animationType = pluginClass.CallStatic<string>("getMessage");
animationType = "Null,Boxing,Backflip";
split = animationType.Split(',');
anim = gameObject.GetComponentInChildren<Animator>();
arrayLength = split.Length;
// Call here
StartCoroutine(LoopThroughAnimation());
}
IEnumerator LoopThroughAnimation ()
{
for (int i = 1; i < arrayLength; i++)
{
animationType = split[i];
Debug.Log(animationType);
//anim.SetInteger ("AnimPar", 0);
anim.Play(animationType);
yield return new WaitForSeconds(10);
}
}
}

Unity what's wrong with my instantiating algorithm?

I dont know if I can call this algorithm. But I am working on a game in which player will move in a circular path.
As you can see in the picture player is suppose to orbit the circle. And obstacle shall be instantiated in the circle.I am trying to first create the obstacle in first half(left to the long cube) and then in the second half. But things are getting created in the next half too when code is not supposed to do that. Also, it is showing argument exception error. Please have a look at my code and tell me whether my method is wrong or my formulas are wrong or anything else.
public class ObjectInstantiater : MonoBehaviour {
DataHolder dataholder;
GameObject Obstacle;
LevelData leveldata;
private int currentlevel=0; // default level starts from 0
private List<GameObject> Inactivegameobject = new List<GameObject>(); // this object can be used
private List<GameObject> Activegameobject = new List<GameObject>();
private int totalgameobjects;
private int firsthalfgameobjects, secondhalfgameobjects;
public float outerradius;
public float innerradius;
private bool shallspawnouterradiues = true;
// Use this for initialization
void Awake () {
dataholder = (Object)GameObject.FindObjectOfType<DataHolder>() as DataHolder;
Obstacle = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
leveldata = dataholder.Leveldata[0];
}
void Start()
{
Updateleveldata();
FirstHalf();
}
public int Currentlevel
{
get { return currentlevel; }
set { currentlevel = value;
leveldata = dataholder.Leveldata[currentlevel];//sets the level data
}
}
private void Updateleveldata() // this function gets called after a round
{
totalgameobjects = Random.Range(leveldata.MinimumObstacle, leveldata.MaximumObstacle);
firsthalfgameobjects = Mathf.RoundToInt(totalgameobjects / 2);
secondhalfgameobjects = totalgameobjects - firsthalfgameobjects;
}
private void FirstHalf()
{
Debug.Log(firsthalfgameobjects);
Vector3 pos;
if (Inactivegameobject.Count < firsthalfgameobjects)
{
for (int x = 0; x <= (firsthalfgameobjects - Inactivegameobject.Count); x++)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
float spawnangledivision = 180 / firsthalfgameobjects;
float spawnangle = 180f;
for(int x = 0; x < firsthalfgameobjects; x++)
{
float proceduralRandomangle = spawnangle;
proceduralRandomangle = Random.Range(proceduralRandomangle , proceduralRandomangle + 2f);
if (shallspawnouterradiues)
{
pos = new Vector3(outerradius * Mathf.Cos(spawnangle), outerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = false;
}else
{
pos = new Vector3(innerradius * Mathf.Cos(spawnangle), innerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = true;
}
spawnangle += spawnangledivision;
Inactivegameobject[0].SetActive(true); // set it to 0
Inactivegameobject[0].transform.position = pos;
Activegameobject.Add(Inactivegameobject[0]);
Inactivegameobject.RemoveAt(0);
}
}
private void SecondHalf()// No need to check this
{
if (Inactivegameobject.Count < firsthalfgameobjects)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
}

How can I reset a Vector3 position?

I'm attempting to write a script that instantiates a row of prefabs. This worked the first time, but every time after that it instantiates one at 0x,0y,0z and the rest ~30x-40x away. I tried setting the initial position before the for loop executes and then resetting the position using the initalPos variable, but that doesn't seem to work. in my code
public class generator : MonoBehaviour {
public int height = 0;
public int width = 0;
private Vector3 temp;
public GameObject sprite;
private Vector3 initialPos;
void Start ()
{
initialPos = new Vector3(0,0,0);
for(int i = 0; i < width; i++)
{
Instantiate (sprite, temp, Quaternion.identity);
temp = sprite.transform.position;
temp.x += 0.089f;
sprite.transform.position = temp;
}
temp = initialPos;
}
}
The temp variable is what I'm setting the current position to so I can add 0.089 to it so that my sprites will line up. I'm trying to reset that value so that they line up starting at 0x every time.
You can simplify your code by specifying the number of prefabs you wish to spawn in a row, rather than the exact width.
Additionally, instead of incrementing your spawn position x value by a hard coded number, use gameObject.transform.localScale.x instead.
For example:
public GameObject Cube;
void Start()
{
SpawnRow(Vector3.zero, 10);
}
void SpawnRow(Vector3 startPosition, int RowLength)
{
Vector3 currentPos = startPosition;
for (int i = 0; i < RowLength; i++)
{
Instantiate(Cube, currentPos, Quaternion.identity);
currentPos.x += Cube.transform.localScale.x;
}
}
Additionally, if you wanted to do something like spawn additional rows next to your each other, you could call SpawnRow() like this:
void Start()
{
Vector3 currentPos = Vector3.zero;
for (int i = 0; i < 3; i++)
{
SpawnRow(currentPos, 10);
currentPos.z += Cube.transform.localScale.z;
}
}
This would give you three rows of 10 gameObjects directly next to each other.

Categories