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;
}
}
}
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
}
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.
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;
}
I'm trying to get my enemies to follow the player and stop within 20 pixels, I have tried a number of algorithms including the Vector2.Lerp(); method to try and fix this but it keeps breaking the build. Any help would be greatly appreciated. The code is below.
public void Update(GameTime gameTime)
{
if (this.IsAlive)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
double distanceToPlayer = Math.Sqrt(Math.Pow(Level.Player.Position.X - this.Position.X, 2) + Math.Pow(Level.Player.Position.Y - this.Position.Y, 2));
// Calculate tile position based on the side we are walking towards.
float posX = Position.X + localBounds.Width / 2 * (int)direction;
int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
int tileY = (int)Math.Floor(Position.Y / Tile.Height);
if (waitTime > 0)
{
// Wait for some amount of time.
waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
if (waitTime <= 0.0f)
{
// Then turn around.
direction = (FaceDirection)(-(int)direction);
}
}
else
{
// If we are about to run into a wall or off a cliff, start waiting.
if (Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Impassable || Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable) //is the enemy is close and is not attacking, attack and turn!
{
waitTime = MaxWaitTime;
}
else
{
// Move in the current direction.
Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
position = position + velocity;
}
}
dtAttack += gameTime.ElapsedGameTime;
AttackPlayer();
}
else
{
dt += gameTime.ElapsedGameTime;
if (dt.TotalSeconds > (sprite.Animation.FrameCount * sprite.Animation.FrameTime))
this.Remove = true;
}
}
Does it have to be 20 pixels on the screen? Seems strange.
You could try to calculate the euclidean distance between the player and the enemy, using the Vector2.Distance method. If the distance is 20 or lower, stop the enemy. If not, keep following the player.