Increasing invoke spawn speed using enumerator - c#

I'm trying to increase the spawn time of an invoke function, I'm using an IEnumerator but it's not working.
It looks like the system reads the starting spawnSpeed value and not changing it reading the IE function.
I'm not aware if there is another way to make this work.
Plz, help.
this is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstaclesController : MonoBehaviour
{
public GameObject obstacle;
private GameObject obstacleClone;
public Transform ObstacleMarker;
private float CloneLifeTime = 4f;
float spawnSpeed;
float spawnInterval = 3;
float minSpawn = -4;
float maxSpawn = 0;
void Start()
{
spawnSpeed = 2f;
StartCoroutine("IncreaseSpeedWithInterval");
InvokeRepeating("AddingObstacle", spawnSpeed, spawnSpeed);
}
IEnumerator IncreaseSpeedWithInterval()
{
while (true)
{
yield return new WaitForSeconds(spawnInterval);
spawnSpeed *= 1.5f;
}
}
private void AddingObstacle()
{
float randomLocation = Random.Range(minSpawn, maxSpawn);
obstacleClone = Instantiate(obstacle, ObstacleMarker.position, ObstacleMarker.rotation);
obstacleClone.transform.position = new Vector3(ObstacleMarker.transform.position.x, randomLocation, ObstacleMarker.transform.position.z);
Destroy(obstacleClone, CloneLifeTime);
}
private void Update()
{
Debug.Log(spawnSpeed);
}
}

After the call to
InvokeRepeating("AddingObstacle", spawnSpeed, spawnSpeed);
any later change on spawnSpeed has no effect whatsoever .. a float is passed by value and there is no connection between the passed one and the one you later adjust!
You can instead wrap it into another Coroutine like
private IEnumerator AddingObstacleRoutine()
{
while(true)
{
yield return new WaitForSeconds (spawnSpeed);
AddingObstacle();
}
}
And instead start this routine as well using
StartCoroutine(IncreaseSpeedWithInterval());
StartCoroutine(AddingObstacleRoutine());

Related

Yield Return New breaking a line of code?

I'm trying to create an ability where you dash through a wall, so to do this I'm dashing and then disabling the colliders for a layer which is going to have everything you can dash through on it. When I add the 'yield return new WaitForSeconds' function, you no longer dash.
I don't have a clue what to do...
using Player;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Phase_Through_Walls : MonoBehaviour
{
public float cooldowntime = 1;
public float nextPhaseTime = 0;
public float currentDashTime = 0.0f;
Movement moveScript;
public float dashSpeed;
public float dashTime;
void Start()
{
moveScript = GetComponent< Movement>();
}
// Update is called once per frame
void Update()
{
if (Time.time > nextPhaseTime)
{
if (Input.GetKeyDown("e"))
{
StartCoroutine(Dash());
}
}
IEnumerator Dash()
{
float startTime = Time.time;
while(Time.time < startTime + dashTime)
{
moveScript.cc.Move(moveScript.moveDirection * dashSpeed * Time.deltaTime); //dashes
Physics.IgnoreLayerCollision(0, 8); //turns off colliders for a certain layer
yield return new WaitForSeconds(1); //waits a second
Physics.IgnoreLayerCollision(0, 8, false); //re enables the collider
yield return null; //returns a null value
}
}
}
}
You need to split up the responsibilities of your methods. Right now you have 1 method trying to accomplish 2 things and it is stepping on itself in the process. Once the yield return new WaitForSeconds(1); is called, control is not returned to the coroutine until that second is finished. So your "dashing" script has to wait for a second as well.
Instead make 2 separate coroutines. 1 of them to actually do the dash, and one for handling collisions. And call them at the same time as below. Also it looked like your missing a closing bracket for your Update method.
using Player;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Phase_Through_Walls : MonoBehaviour
{
public float cooldowntime = 1;
public float nextPhaseTime = 0;
public float currentDashTime = 0.0f;
Movement moveScript;
public float dashSpeed;
public float dashTime;
void Start()
{
moveScript = GetComponent< Movement>();
}
// Update is called once per frame
void Update()
{
if (Time.time > nextPhaseTime)
{
if (Input.GetKeyDown("e"))
{
DoDash();
}
}
}
void DoDash()
{
Physics.IgnoreLayerCollision(0, 8); //disable colliders BEFORE dashing initiates
StartCoroutine(Dash());
StartCoroutine(RestoreCollision());
}
IEnumerator Dash()
{
float startTime = Time.time;
while(Time.time < startTime + dashTime)
{
moveScript.cc.Move(moveScript.moveDirection * dashSpeed * Time.deltaTime); //dashes
yield return null; // waits for next frame
}
}
IEnumerator RestoreCollision()
{
yield return new WaitForSeconds(1); //waits a second
Physics.IgnoreLayerCollision(0, 8, false); //re enables the collider
}
}

What to do when VSCode is not giving me an error?

I'm new to C# (did some stuff in python earlier) and i cant get this code to work. Im making a mobile game and this script should run a timer, check if the timer is equal to "SaleTime" and if it is add money to the users balance and reset the timer to 0.
As VSCode is not giving me any errors i dont know what the problem is and after looking around i cant find a solution to it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Threading;
public class Sales : MonoBehaviour
{
public float Timer = 0.0f;
public float SaleTime = 5.0f;
public float ProductValue = 5.0f;
public float Money = 1000.0f;
void Start()
{
StartCoroutine(time());
}
public void GameTime()
{
Timer += 1;
}
IEnumerator time()
{
while (true)
{
GameTime();
yield return new WaitForSeconds(1);
}
}
public void SaleFunction()
{
if (Timer == SaleTime)
{
Timer = 0.0f;
Money = Money + ProductValue;
}
}
}
You are not calling SaleFunction(), so the program never checks the condition.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Threading;
public class Sales : MonoBehaviour
{
public float Timer = 0.0f;
public float SaleTime = 5.0f;
public float ProductValue = 5.0f;
public float Money = 1000.0f;
void Start()
{
StartCoroutine(time());
}
public void GameTime()
{
Timer += 1;
}
IEnumerator time()
{
while (true)
{
GameTime();
SaleFunction(); // Added line.
yield return new WaitForSeconds(1);
}
}
public void SaleFunction()
{
if (Timer == SaleTime)
{
Timer = 0.0f;
Money = Money + ProductValue;
}
}
}

How can I start the coroutine with time?

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMouthSpeechController : MonoBehaviour
{
public TMP_Text[] texts;
public bool startTalking = false;
public float talkTime;
public float duration;
[Range(0, 100)]
public float valueRange;
private SkinnedMeshRenderer bodySkinnedMeshRenderer;
//private bool isTalking = true;
// Start is called before the first frame update
void Start()
{
bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
}
// Update is called once per frame
void Update()
{
/*if (startTalking && isTalking)
{
StartCoroutine(AnimateMouth());
StartCoroutine(TalkTime());
isTalking = false;
}
if(startTalking == false && isTalking == false)
{
isTalking = true;
}*/
}
//Lerp between startValue and endValue over 'duration' seconds
private IEnumerator LerpShape(float startValue, float endValue, float duration)
{
float elapsed = 0;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
yield return null;
}
}
//animate open and closed, then repeat
public IEnumerator AnimateMouth()
{
while (startTalking == true)
{
yield return StartCoroutine(LerpShape(0, valueRange, duration));
yield return StartCoroutine(LerpShape(valueRange, 0, duration));
}
}
public IEnumerator TalkTime()
{
yield return new WaitForSeconds(talkTime);
startTalking = false;
}
}
I messed it all before with too many flags and in the Update() messed it too.
I want to make something simple. To be able to call from any other script to the method AnimateMouth and that the method AnimateMouth will also get a float will be the time the mouth will be animated. something like :
StartCoroutine(AnimateMouth(4f));
but I messed it all. maybe to make the AnimateMout public static or when calling from another script first to make a reference to the PlayerMouthSpeechController and then call it something like :
playerMouthSpeechController.AnimateMouth();
and something will start the AnimatedMouth coroutine. but again I messed it too much.
Update :
This is working almost perfectly as I wanted still I need to use StartCoroutine each time I want the player to start talking but it's working.
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMouthSpeechController : MonoBehaviour
{
public TMP_Text[] texts;
public float duration;
[Range(0, 100)]
public float valueRange;
private bool startTalking = false;
private SkinnedMeshRenderer bodySkinnedMeshRenderer;
// Start is called before the first frame update
void Start()
{
bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
}
// Update is called once per frame
void Update()
{
}
//Lerp between startValue and endValue over 'duration' seconds
private IEnumerator LerpShape(float startValue, float endValue, float duration)
{
float elapsed = 0;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
yield return null;
}
}
//animate open and closed, then repeat
public IEnumerator AnimateMouth(float TimeToTalk)
{
startTalking = true;
StartCoroutine(TalkTime(TimeToTalk));
while (startTalking == true)
{
yield return StartCoroutine(LerpShape(0, valueRange, duration));
yield return StartCoroutine(LerpShape(valueRange, 0, duration));
}
}
private IEnumerator TalkTime(float TalkTime)
{
yield return new WaitForSeconds(TalkTime);
startTalking = false;
}
}
And using it for example in another script :
At the top :
public PlayerMouthSpeechController blendShapeController;
And
StartCoroutine(blendShapeController.AnimateMouth(10f));
I wish I could do somehow that I will not start a coroutine each time and that it will start the coroutine automatic and I will only do :
blendShapeController.AnimateMouth(10f);
And it will start the coroutine in the PlayerMouthSpeechController.
Working solution as I wanted :
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMouthSpeechController : MonoBehaviour
{
public TMP_Text[] texts;
public float duration;
[Range(0, 100)]
public float valueRange;
private bool startTalking = false;
private SkinnedMeshRenderer bodySkinnedMeshRenderer;
// Start is called before the first frame update
void Start()
{
bodySkinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
}
// Update is called once per frame
void Update()
{
}
//Lerp between startValue and endValue over 'duration' seconds
private IEnumerator LerpShape(float startValue, float endValue, float duration)
{
float elapsed = 0;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float value = Mathf.Lerp(startValue, endValue, elapsed / duration);
bodySkinnedMeshRenderer.SetBlendShapeWeight(0, value);
yield return null;
}
}
//animate open and closed, then repeat
public void AnimateMouth(float TimeToTalk)
{
StartCoroutine(StartAnimating(TimeToTalk));
}
private IEnumerator StartAnimating(float TimeToTalk)
{
startTalking = true;
StartCoroutine(TalkingTime(TimeToTalk));
while (startTalking == true)
{
yield return StartCoroutine(LerpShape(0, valueRange, duration));
yield return StartCoroutine(LerpShape(valueRange, 0, duration));
}
}
private IEnumerator TalkingTime(float TalkTime)
{
yield return new WaitForSeconds(TalkTime);
startTalking = false;
}
}
In other script/s making a reference :
public PlayerMouthSpeechController blendShapeController;
And starting
blendShapeController.AnimateMouth(10f);

Unity wait for WWW to finish without coroutine [duplicate]

How can you pass a Monobehaviour inside an instance of a non Monobehaviour class? I found this link where TonyLi mentions that you can pass a Monobehaviour to start and stop coroutines inside a instance of a class, but he does not show how you can do that. He does this theEvent.StartEvent(myMonoBehaviour); but he does not show where he gets myMonobehaviour from. I looked around on the internet but I cannot seem to find how.
Edit
Here is what I am trying to do. I want to run a coroutine inside an instance of a class. I also want to be able to stop the coroutine inside the instance of the class. I want to do it this way so that I don't have any objects in my scene that have large managers and also so that I can reuse the code for any object that I want to pingpong in this way. The code moves a Gameobject in one direction then takes a break and moves it in the other direction and takes a break again etc. But I cannot start the coroutine from outside the class.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
[RequireComponent (typeof(Image))]
public class SpecialBar : MonoBehaviour {
public float rangeX;
public float breakTime;
public float step;
float startProgress = 0.5f;
PingPongGameObject pingPonger;
Color[] teamColors = new Color[]{new Color(255,136,0),new Color(0,170,255)};
void Start()
{
for(int i = 0; i < teamColors.Length; ++i)
{
teamColors[i] = StaticFunctions.NormalizeColor (teamColors[i]);
}
pingPonger = new PingPongGameObject (gameObject.transform.position,
new Vector3(rangeX,0.0f,0.0f),
gameObject,
startProgress,
breakTime,
step
);
}
}
The second class is where my coroutine is in.
public class PingPongGameObject
{
float step;
Vector3 center;
Vector3 range;
GameObject ball;
float progress;
float breakTime;
Vector3 endPos;
Vector3 oppositePosition;
public PingPongGameObject(Vector3 _center, Vector3 _range, GameObject _ball, float _startProgress, float _breakTime, float _step)
{
center = _center;
range = _range;
ball = _ball;
progress = _startProgress;
breakTime = _breakTime;
step = _step;
endPos = center - range;
oppositePosition = center + range;
// This is where I want to start the coroutine
}
public IEnumerator PingPong()
{
while (progress < 1) {
progress += Time.deltaTime * step;
Vector3 newPos = Vector3.Lerp (oppositePosition, endPos, progress);
ball.transform.position = newPos;
yield return null;
}
Vector3 temp = endPos;
endPos = oppositePosition;
oppositePosition = temp;
progress = 0;
yield return new WaitForSeconds (breakTime);
yield return null;
}
public float Step
{
set{step = value;}
}
public void StopCoroutine()
{
// This is where I want to stop the coroutine
}
}
TonyLi mentions that you can pass a Monobehaviour to start and stop
coroutines inside a instance of a class, but he does not show how you
can do that. He does this
You are can do that with the this keyword. The this keyword will get the current instance of MonoBehaviour.
In this example there's a tree, which happens to have a component MonoScript:
That particular instance of MonoScript can if it wants (since it's a c# program) instantiate a general c# class, NonMonoScript:
Class to pass MonoBehaviour from:
public class MonoScript : MonoBehaviour
{
void Start()
{
NonMonoScript nonMonoScript = new NonMonoScript();
//Pass MonoBehaviour to non MonoBehaviour class
nonMonoScript.monoParser(this);
}
}
Class that receives pass MonoBehaviour instance:
public class NonMonoScript
{
public void monoParser(MonoBehaviour mono)
{
//We can now use StartCoroutine from MonoBehaviour in a non MonoBehaviour script
mono.StartCoroutine(testFunction());
//And also use StopCoroutine function
mono.StopCoroutine(testFunction());
}
IEnumerator testFunction()
{
yield return new WaitForSeconds(3f);
Debug.Log("Test!");
}
}
You can also store the mono reference from the monoParser function in a local variable to be re-used.

Unity - How can I use a flag to decide whether all the objects rotate at once or each one individually?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SpinableObject
{
[SerializeField]
private Transform t;
[SerializeField]
private float rotationSpeed;
[SerializeField]
private float minSpeed;
[SerializeField]
private float maxSpeed;
[SerializeField]
private float speedRate;
private bool slowDown;
public void Rotate()
{
if (rotationSpeed > maxSpeed)
slowDown = true;
else if (rotationSpeed < minSpeed)
slowDown = false;
rotationSpeed = (slowDown) ? rotationSpeed - 0.1f : rotationSpeed + 0.1f;
t.Rotate(Vector3.forward, Time.deltaTime * rotationSpeed);
}
}
public class SpinObject : MonoBehaviour
{
private bool slowDown = false;
private GameObject[] allPropellers;
public bool rotateAll = false;
public float rotationSpeed;
public float slowdownMax;
public float slowdownMin;
public SpinableObject[] objectsToRotate;
// Use this for initialization
void Start()
{
allPropellers = GameObject.FindGameObjectsWithTag("Propeller");
}
// Update is called once per frame
void Update()
{
if (rotateAll == false)
{
for (int i = 0; i < objectsToRotate.Length; i++)
{
objectsToRotate[i].Rotate();
}
}
else
{
objectsToRotate = new SpinableObject[allPropellers.Length];
for (int i = 0; i < allPropellers.Length; i++)
{
objectsToRotate[i].Rotate();
}
}
}
}
In the else in this part I want that all the objects will rotate with the global variables settings. And if the rotateAll is false each one will rotate with their own options settings.
objectsToRotate = new SpinableObject[allPropellers.Length];
for (int i = 0; i < allPropellers.Length; i++)
{
objectsToRotate[i].Rotate();
}
But here I'm only make instance for more places in the objectsToRotate they are all null. And I'm not sure using objectsToRotate is good to rotate them all at once.
Update: This is what i tried now:
I changed the SpinableObject script to:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SpinableObject
{
public Transform t;
public float rotationSpeed;
public float minSpeed;
public float maxSpeed;
public float speedRate;
public bool slowDown;
}
public class SpinObject : MonoBehaviour
{
public SpinableObject[] objectsToRotate;
private Rotate _rotate;
private int index = 0;
private bool rotateAll;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
var _objecttorotate = objectsToRotate[index];
_rotate.t = _objecttorotate.t;
_rotate.rotationSpeed = _objecttorotate.rotationSpeed;
_rotate.minSpeed = _objecttorotate.minSpeed;
_rotate.maxSpeed = _objecttorotate.maxSpeed;
_rotate.speedRate = _objecttorotate.speedRate;
_rotate.slowDown = _objecttorotate.slowDown;
index++;
}
}
And created a new script name Rotate:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate : MonoBehaviour
{
public Transform t;
public float rotationSpeed;
public float minSpeed;
public float maxSpeed;
public float speedRate;
public bool slowDown;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
RotateObject();
}
public void RotateObject()
{
if (rotationSpeed > maxSpeed)
slowDown = true;
else if (rotationSpeed < minSpeed)
slowDown = false;
rotationSpeed = (slowDown) ? rotationSpeed - 0.1f : rotationSpeed + 0.1f;
t.Rotate(Vector3.forward, Time.deltaTime * rotationSpeed);
}
}
The idea is to feed the variables and settings in the script Rotate from the script SpinableObject.
But i messed it up it's not working and give me some null exception.
Is it a good way ? And how can i fix the scripts to work with each other so the SpinableObject will feed the Rotate with data.
This is how I would approach your problem.
I will define a global variable to determine if objects are rotating all at the same time or independently.
Then I will create a script called rotation and I will add it to each GameObject in your scene which will rotate.
In this script I will just include the
...
Update()
{
if (rotateAll == false)
{
Rotate(allProperties)
}else{
Rotate(particularProperties)
}
}
//Here you can implement the Rotate function passing the attributes you consider to make the rotation different for each case
...
And about global variables in Unity. One possible approach is to define a class like:
public static class GlobalVariables{
public static boolean rotationType;
}
Then you can access and modigy this variable from another script like:
GlobalVariables.rotationType = true;
if(GlobalVariables.rotationType){
...
}
After Edit:
I canĀ“t really tell what you are doing wrong, but if there is a null exception it may be you are not calling properly from the script the gameObjects you want to rotate. It could be you are not linking them correctly in the inspector. If you have declare
public SpinableObject[] objectsToRotate;
It is possible you forgot to drag and drop in the inspector the GamesObjets into the array to populate it. But I can't be sure if the problem is just that.

Categories