l am working on a mobile endless running game on unity.My character's speed is equal to a var called "speed":
var speed = 7;
GetComponent<Rigidbody> ().velocity = new Vector3 (0, 0, speed);
and l want that var (speed) to increase by 4 every 30 seconds using C#. Any ideas?
There are many ways to do this in Unity.
1.In the Update function with Time.deltaTime by using it to increment a variable
int speed = 7;
float counter = 0;
void Update()
{
//Increment Counter
counter += Time.deltaTime;
if (counter >= 30)
{
//Increment Speed by 4
incrementSpeed();
//RESET Counter
counter = 0;
}
}
void incrementSpeed()
{
speed += 4;
}
2.With coroutine and WaitForSeconds or WaitForSecondsRealtime.
void Start()
{
StartCoroutine(incremental());
}
IEnumerator incremental()
{
while (true)
{
//Wait for 30 seconds
yield return new WaitForSeconds(30);
//Increment Speed
incrementSpeed();
}
}
void incrementSpeed()
{
speed += 4;
}
Which one to use really depends on if you want to see the counter or not. With the second solution, you can't see the status of the timer. It will just increment after every 30 seconds. These are the most basic ways.
Related
I want to simulate health regeneration in my game in unity, in the function RestoreHealth().
Am I overthinking it by wanting to create a child process, so when I call wait, it won't affect any current running process or thread and the child process will die when the function is done.
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest
//tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
health += 10;
// sleep(1000); // make function wait for '1 second' to iterate again
Debug.Log("Health: " + health);
}
}
How do I create a child process in C# or unity in this case and cause it to wait?
Is there an equivalent to Fork(); like in C?
Also, this function is called when the player initially receives damage.
Solution:
note: I changed Health to Armour
public IEnumerator RestoreArmour() {
while (_Armour < _MaxArmour) {
_Armour++;
Debug.Log("Health: " + _Armour);
yield return new WaitForSeconds(ArmourRegenRate); // ArmourRegenRate is a
// float for the seconds
}
}
and use this to initiate the coroutine
void Start(){
StartCoroutine(player.RestoreArmour());
}
Basic Coroutine concept
In Unity you work with Coroutines to achieve this asychronous "threaded" behaviour
IEnumerator RestoreHealth() {
while (health != MaxHealth) {
health++;
yield return new WaitForEndOfFrame();
}
}
and then invoke it with
StartCoroutine(RestoreHealth());
Restarting a Coroutine
In order to stop an existing Coroutine from running and start a new one, this is how you would achieve that:
private Coroutine _myCoroutine = null;
void SomeMethod()
{
if (_myCoroutine != null)
StopCoroutine(_myCoroutine);
_myCoroutine = StartCoroutine(SomeOtherMethod());
}
Pausing armor restoration for X seconds when player has taken damage
A common functionality is to have something restore armor when player hasn't taken damage for X seconds:
private bool _shouldRestoreArmour = true;
private Coroutine _pauseArmorCoroutine = null;
void Update()
{
if (_shouldRestoreArmour)
Armor += ArmorRegenerationPerSecond * Time.deltaTime;
}
void PlayerTakeDamage()
{
if (_pauseArmorCoroutine != null)
StopCoroutine(_pauseArmorCoroutine);
_pauseArmorCoroutine = StartCoroutine(PauseRestoreArmor());
// Take damage code
}
IEnumerator PauseRestoreArmor()
{
_shouldRestoreArmor = false;
yield return new WaitForSeconds(RESTORE_ARMOR_DELAY_TIME);
_shouldRestoreArmor = true;
}
Here the player will regenerate armor at all times, except for X seconds after the player has taken damage. If the player takes damage multiple times we will simply abort the previous coroutine and start a new one, so that it will be a fresh X seconds from the last hit.
You can create a timer assuming that you run this in the update method
private float timer = 1;
private float timerReset = 1;
private void Update(){
RestoreHealth();
}
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
if(timer > 0){
timer -= 1 * time.deltaTime;
}else{
health += 10;
Debug.Log("Health: " + health);
timer = timerReset;
}
}
}
or you can simply just give your player 1 health every second or whatever amount for instance
public void RestoreHealth() {
if (health >= MaxHealth) return; // health and MaxHealth are Class variables
if (health % 10 != 0) { // if health is not dividable by 10
int temp = health % 10; // then we round it to the closest tenth
temp = 10 - temp;
health += temp;
}
int i = health;
for (; i < MaxHealth; i += 10) { // where the health grows till 100
health += 10 * 1 * time.deltaTime;
Debug.Log("Health: " + health);
timer = timerReset;
}
}
So in this scenario your player recieves 10 health every second but the value will always go up so in the matter of a second the player health would go up 1 health every 100ms
I'm working on a school project where a 'player' is moving next to a wall. This wall is made of cubes (1x1x1). These cubes have cubes in them (0.9x0.9x0.9) which move outwards, towards the player when the player moves next to them.
This animation now moves every 1 frame. which is kinda laggy and unnatural.
I would like this animation to move every 5 frames.
using UnityEngine;
using System.Collections;
public class InteractiefBlokje : MonoBehaviour {
private Transform thePlayer;
private Transform binnenBlokje;
// Use this for initialization
void Start () {
// referentie naar binnenblokje
binnenBlokje = this.gameObject.transform.GetChild(0);
// referentie naar de 'player'
thePlayer = GameObject.FindGameObjectWithTag("Player").transform;
Debug.Log(thePlayer);
}
// Update is called once per frame
void Update () {
Vector3 myPosition = this.transform.position;
Vector3 playerPosition = thePlayer.position;
// afstand tussen player en dit blokje
float distance = Mathf.Clamp(Vector2.Distance(new Vector2(myPosition.x, myPosition.z), new Vector2(playerPosition.x, playerPosition.z)), 0, 50);
// bij afstand 3 -> x = 0.8
// bij afstand 5 -> x = 0
binnenBlokje.position = new Vector3(Random.Range(0, (distance - 5.0f) * -0.4f), this.transform.position.y, this.transform.position.z);
}
}
Well, if you want to count the frames, you can use a counter, such as :
int FrameCounter = 5;
void Update () {
if (FrameCounter++ % 5 == 0)
{
// your animation goes there
}
}
or
int FrameCounter = 5;
void Update () {
if (FrameCounter++ >= 5)
{
FrameCounter = 1;
// your animation goes there
}
}
But since there is time differences between each frames (FPS can drop/increases), you might want to use the time.
float timeBetweenAnimations = 0.1f; //0.1 second, arbitrary value
float timer = timeBetweenAnimations;
void Update () {
timer += Time.deltaTime; // increase the timer by the time elapsed since last frame
if (timer >= timeBetweenAnimations)
{
timer = 0; // reset the timer to 0
// your animation goes there
}
}
Or, you can use that timer and a speed to define a distance (distance = speed * time)
float timer;
float speed = 2.0f; // arbitrary value of 2 units per seconds
void Update () {
timer = Time.deltaTime; // set the timer by the time elapsed since last frame
var direction = new Vector3(...); // the direction you want your object to move, replace the ... by the real vector you need
theObjectToMove.transform.position = direction * speed * timer; // feel free to add a * randomValue to increase/decrease randomly that speed
}
I want to create some fireflies in Unity. I want to Increase light intensity then wait some seconds and then decrease it in Unity. When they get spawned, I want them increasing their light intensity, wait some seconds and then fade out. How can I create this "process" in a clean way?
private Light pointLight; // The light component of the firefly
private float minLuminosity = 0; // min intensity
private float maxLuminosity = 1; // max intensity
private float luminositySteps = 0.005f; // factor when increasing / decreasing
private float shineDuration = 3; // wait 3 seconds when faded in
private void Start()
{
pointLight = GetComponent<Light>();
pointLight.intensity = Random.Range(minLuminosity, maxLuminosity); // start with a random intensity
StartCoroutine(ChangeIntensity()); // start the process
}
private IEnumerator ChangeIntensity()
{
pointLight.intensity += luminositySteps; // increase the firefly intensity / fade in
yield return new WaitWhile(() => pointLight.intensity >= maxLuminosity); // wait for the maximum intensity
yield return new WaitForSeconds(shineDuration); // wait 3 seconds
pointLight.intensity -= luminositySteps;
yield return new WaitWhile(() => pointLight.intensity <= maxLuminosity); // wait for the minimum intensity
StartCoroutine(ChangeIntensity()); // do it again
}
So obviously the coroutine stops forever at the first WaitWhile() How can I create such a code chain? When fading in or out, I just mean changing the light intensity.
Even though this has been solved, the current solutions are just decrementing the variable and also creates new object (WaitForSeconds) every frame.
The proper way of doing this in Unity is using Mathf.Lerp and Time.deltaTime. This type of operation is what Mathf.Lerp is made for which is to go from your minLuminosity to maxLuminosity. You can read more about this in my other question for fading out/in GameObject using its alpha component here.
I took the fadeInAndOut function from that answer and ported it to work with the Light component. Here is a simple light fade in/out function:
IEnumerator fadeInAndOut(Light lightToFade, bool fadeIn, float duration)
{
float minLuminosity = 0; // min intensity
float maxLuminosity = 1; // max intensity
float counter = 0f;
//Set Values depending on if fadeIn or fadeOut
float a, b;
if (fadeIn)
{
a = minLuminosity;
b = maxLuminosity;
}
else
{
a = maxLuminosity;
b = minLuminosity;
}
float currentIntensity = lightToFade.intensity;
while (counter < duration)
{
counter += Time.deltaTime;
lightToFade.intensity = Mathf.Lerp(a, b, counter / duration);
yield return null;
}
}
Now, to create the exact effect you want which is to increase light intensity then wait some seconds and then decrease it, create another coroutine function that calls the function above and waits for it to finish. You can do that by yielding the fadeInAndOut function. Notice how WaitForSeconds is declared outside the while loop so that it does not create new Object each time.
//Fade in and out forever
IEnumerator fadeInAndOutRepeat(Light lightToFade, float duration, float waitTime)
{
WaitForSeconds waitForXSec = new WaitForSeconds(waitTime);
while (true)
{
//Fade out
yield return fadeInAndOut(lightToFade, false, duration);
//Wait
yield return waitForXSec;
//Fade-in
yield return fadeInAndOut(lightToFade, true, duration);
}
}
USAGE:
public Light lightToFade;
public float eachFadeTime = 2f;
public float fadeWaitTime = 5f;
void Start()
{
StartCoroutine(fadeInAndOutRepeat(lightToFade, eachFadeTime, fadeWaitTime));
}
The problem in your code was that you applied your luminosity change only once, thus your WaitWhile condition would never be reached. I would change both WaitWhile into simple while loops, and then use WaitForEndOfFrame:
private IEnumerator ChangeIntensity()
{
while(true)
{
while(pointLight.intensity <= maxLuminosity)
{
pointLight.intensity += luminositySteps; // increase the firefly intensity / fade in
yield return new WaitForEndOfFrame();
}
yield return new WaitForSeconds(shineDuration); // wait 3 seconds
while(pointLight.intensity > minLuminosity)
{
pointLight.intensity -= luminositySteps;
yield return new WaitForEndOfFrame();
}
}
}
So I got it by using the following while loop
private IEnumerator ChangeIntensity()
{
while (true)
{
pointLight.intensity += isIncreasing ? luminositySteps : -luminositySteps;
if (pointLight.intensity <= minLuminosity)
isIncreasing = true;
if (pointLight.intensity >= maxLuminosity)
{
isIncreasing = false;
yield return new WaitForSeconds(shineDuration);
}
yield return null;
}
}
In Unity, I am facing a small problem. I start some spawns at a certain speed (let's say 2) and by time, the speed increases (let's say by 0.5). When the player loses and the Game Over screen is loaded, the speed continues to increase in the background, so when the player tries again, the speed isn't 2 (initial/normal) but it's 3 or 4 or (depending on how much time has passed).
How can I fix this? Should I destroy or disable the execution of the scripts when Gameover screen is loaded?
Code:
public class ObjectsScroller : MonoBehaviour {
public float speed = 2; // Speed of the rings
int MaxSpeed = 5; // Max speed of the rings
int timeSecond = 1; // 1s
float timer;
int sixSeconds = 6; // 6 seconds without subtraction
int countdown = 6; // Countdown with subtraction
// Use this for initialization
void Start ()
{
speed = 2;
timeSecond = 1;
timer = Time.time;
countdown = 6;
}
void Update()
{
timer = Time.time;
transform.Translate(Vector2.up * speed * Time.deltaTime);
if(timer >= timeSecond) { // Checks if a second passed
countdown -= 1; // countdown decreases by one
timeSecond += 1; // Increases so the loop runs again
}
if (countdown <= 0) { // Checks if countdown is less than 0
speed += .5f; // Speed increases by 1
transform.Translate(Vector2.up * speed * Time.deltaTime);
countdown = sixSeconds; // Countdown is equal to 8 Seconds so the loop runs again
}
if (speed >= 5) { // If speed is bigger or equal to 5 then make it 5 only
speed = MaxSpeed;
}
}
}
Ok, so I have an animationnthe speed of which is controlled by user's tapping m meaning I cant just lerp for a set time but have to have it depend on if this is true:
cameraAnim.GetCurrentAnimatorStateInfo (0).IsName ("stillOpening")
By the end of this animation (no matter how long it took, fast or slow) I need this float in my material to have lerped to its final value:
skybox.SetFloat ("_Exponent1",Mathf.Lerp(skybox.GetFloat("_Exponent1"), topSkyBoxOpen, ratio));
Meaning it has to be equal to topSkyBoxOpen at the end of "stillOpening". I don't know how to coordinate the timing.
I have tried this in the Update():
void openSkyLerp()
{
float ratio = 0;
float duration = 0.5f; // this is the one that will control how long it takes
// value is in second
float multiplier = 1 / duration;
while (cameraAnim.GetCurrentAnimatorStateInfo (0).IsName ("stillOpening")) {
ratio += Time.deltaTime * multiplier;
skybox.SetFloat ("_Exponent1",Mathf.Lerp(skybox.GetFloat("_Exponent1"), topSkyBoxOpen, ratio));
}
}
But nothing happens at all - I read this might be because its trying to have it all lerp in 1 frame. Is this possible? How can I lerp WHILE an animation is playing regardless of its speed?
Meaning it has to be equal to topSkyBoxOpen at the end of
"stillOpening". I don't know how to coordinate the timing.
The problem is that you are not even waiting for a frame. So, even if your equation is correct, all those cannot happen smoothly in a single frame. You wait for a frame with yield return null; and that requires coroutine.
Answered a very similar but not the-same question few hours ago.
If the stillOpening variable is the destination value, get the current _Exponent1 value before going into the while loop. Have a counter variable that increments each frame while counter is less than topSkyBoxOpen. You can then use Mathf.Lerp(currentVal, topSkyBoxOpen, counter / duration); in the SetFloat function.
bool running = false;
void openSkyLerp()
{
if (running)
{
return;
}
running = true;
StartCoroutine(OpenSky(5));
}
IEnumerator OpenSky(float duration)
{
float currentVal = skybox.GetFloat("_Exponent1");
float counter = 0;
while (counter < topSkyBoxOpen)
{
//Exit if not still opening
if (!cameraAnim.GetCurrentAnimatorStateInfo(0).IsName("stillOpening"))
{
yield break;
}
counter = counter + Time.deltaTime;
float val = Mathf.Lerp(currentVal, topSkyBoxOpen, counter / duration);
skybox.SetFloat("_Exponent1", val);
yield return null; //Wait for a frame
}
running = false;
}