I have a cycle and want the cycle bar, having a value range from 0 to 1 and back to 0.
So, currently I use this code
public class DayNightCycle : MonoBehaviour
{
private float currentTime = 0; // current time of the day
private float secondsPerDay = 120; // maximum time per day
private Image cycleBar; // ui bar
private void Start()
{
cycleBar = GetComponent<Image>(); // reference
UpdateCycleBar(); // update the ui
}
private void Update()
{
currentTime += Time.deltaTime; // increase the time
if (currentTime >= secondsPerDay) // day is over?
currentTime = 0; // reset time
UpdateCycleBar(); // update ui
}
private void UpdateCycleBar()
{
cycleBar.rectTransform.localScale = new Vector3(currentTime / secondsPerDay, 1, 1);
}
}
but now I want a behaviour as mentioned by the picture above. How can I increase currentTime from 0 to 1 and then back to 0?
The problem: My cycle bar should still increase from the left to the right.
The night should last 40% of the maximum time, the other ones 20%.
If you are looking for a way to increase a variable from 0 to 1 then from 1 to 0, Mathf.PingPong is the answer. There are many other ways to do this but Mathf.PingPong is made for tasks like this one.
public float speed = 1.19f;
void Update()
{
//PingPong between 0 and 1
float time = Mathf.PingPong(Time.time * speed, 1);
Debug.Log(time);
}
Do this by Mathf.Sin() function. But you must get absolute value of it. Mathf.abs(mathf.sin());
It will change between 0 to 1 then back to zero. But its not smooth in zero.
Or offset sin function by +1 at the end multiply it by 0.5f to let it back to one again.
float timer = 0;
float cycle = 0;
public float speed = 1;
void Update()
{
timer += Time.deltaTime;
Cycle();
}
void Cycle()
{
cycle = (Mathf.Sin(timer) + 1) * 0.5f;
}
instead of 0 to 1, use -1 to 1.
The timer starts from -1, increase in update function by deltaTime, and then when it will become moreThan equal 1 it will be reset to -1. its a loop...
float timer = -1;
void Update()
{
timer += Time.deltaTime;
if(timer >= 1)
{
timer = -1;
}
Cycle();
}
void Cycle()
{
//Do Your Cycle
//-1 is left night, 0 is middle day, 1 is right night
}
Related
I am trying to set a fire rate in Unity so that when I hold down up arrow, I will shoot a projectile up every 1 second. Currently my code is shooting a projectile every frame update, even though I have a Coroutine set up.
public GameObject bulletPrefab;
public float bulletSpeed;
public float fireRate = 1f;
public bool allowFire = true;
void Update()
{
//shooting input
if (Input.GetKey(KeyCode.UpArrow) && allowFire == true)
{
StartCoroutine(Shoot("up"));
}
}
IEnumerator Shoot(string direction)
{
allowFire = false;
if (direction == "up")
{
var bulletInstance = Instantiate(bulletPrefab, new Vector3(transform.position.x, transform.position.y, transform.position.z + 1), Quaternion.identity);
bulletInstance.GetComponent<Rigidbody>().AddForce(Vector3.forward * bulletSpeed);
}
yield return new WaitForSeconds(fireRate);
allowFire = true;
}
Coroutine
You can use the coroutine, but since you're calling it in an Update loop you much wait for it to finish before starting another one.
Coroutine currentCoroutine;
if(currentCoroutine == null)
currentCoroutine = StartCoroutine(DoShoot());
IEnumerator DoShoot() {
// Shoot.
yield return new WaitForSeconds(1f);
currentCoroutine = null;
}
Timestamp
You can also use a timestamp for when cooldown is ready. It's basically current time plus some duration.
float cooldown = 1f;
float cooldownTimestamp;
bool TryShoot (Vector2 direction) {
if (Time.time < cooldownTimestamp) return false;
cooldownTimestamp = Time.time + cooldown;
// Shoot!
}
I usually end up doing something like:
Variables
[Serializefield] float shootDelay = 1f;
float T_ShootDelay
Start()
T_ShootDelay = shootDelay;
Update()
if(T_ShootDelay < shootDelay)
T_ShootDelay += Time.deltaTime;
ShootInput()
if(T_ShootDelay >= shootDelay)
{
T_ShootDelay = 0;
Shoot();
}
What this does is:
Check if the shootDelay timer is less than the shootDelay.
Add up the timer by 1 per second.
Check the timer's status every time you want to shoot.
Set the timer to 0 after shooting
Considering a M4 AR that fires at least 700 rounds per minute.
700 rounds / 60 (seconds) ~= 11,66 rounds per second
1 second / 11 rounds ~= 0,085 seconds of delay between each round
You could simply try this:
yield return new WaitForSeconds(1 / (fireRate / 60f));
I'm making a game currently and I got stuck while I was thinking about a combotimer.
Now in my game, there are enemies that run through my character and I hit them. Yet, at some point, if I can correctly hit the enemies, I want to add a powerful combo option like hitting them without any difficulty. I thought like I can create a class or a value that keeps the correct hits and let's say it's 5. When it reaches 5, then I can change the hitting options. Yet, where I'm stuck is that how I can identify after how many hits or seconds the combo can end. And in here what came to my mind is that I can make it with time. So here's the thing. I want to detect 5 hits and then I want a combotimer which makes the value of 5 decreased. So that when the value reaches 0 then I can continue to play my game with the normal standards of it. How can I do this?
Since you didn't provide anything here is what I would do as dummy code. This does
Everytime you hit an enemy check the time since last hit
If under the maximum delay => add to combo
If not reset the combo counter and start over
If reaching enough hits => enable isSuperCombo
Over time reset the isSuperCombo
As long as you are isSuperCombo you can still add hits even if they happen after the normal maxTimeBetweenHits to enlarge the duration of isSuperCombo as a little bonus
Something like
public class ComboCounter : MonoBehaviour
{
public bool isSuperCombo;
// Maimum delay in seconds since the hit for counting the current hit as combo
[SerializeField] private float maxTimeBetweenHits = 1;
// Requried hits in one combo in order to activate power bonus
[SerializeField] private int hitsUntilSuperCombo = 5;
// Delay in seconds to reset the powerup after the last hit
[SerializeField] private float powerUpDuration = 5;
private int hitCounter;
private float lastHitTime;
private float powerUpResetTimer;
private void Update()
{
if(isSuperCombo)
{
powerUpResetTimer -= Time.deltaTime;
if(powerUpResetTimer <= 0)
{
isSuperCombo = false;
hitCounter = 0;
}
}
}
// Call when you hit an enemy
public void AddHit()
{
if(Time.time - lastHitTime < maxTimeBetweenHits)
{
// then add to the hit counter
hitCounter++;
if(hitCounter >= hitsUntilSuperCombo)
{
isSuperCombo = true;
powerUpResetTimer = powerUpDuration;
}
}
else
{
// otherwise the delay was too big => not a combo anymore
// => Reset the counter and start over with this hit as the first one
hitCounter = 1;
}
// update the lastHitTime
lastHitTime = Time.time;
}
}
Then you could e.g. in another class check something like
public void CauseDamage(Enemy enemy)
{
enemy.health -= GetComponent<ComboCounter>().isSuperCombo ? 4 : 1;
}
If you need to note 5 seconds of increased damage you can use coroutine
private float _damage = 10;
public void StartCombo(float duration = 5)
{
StartCoroutine(ComboHandler(duration));
}
private IEnumerator ComboHandler(float duration)
{
_damage *= 2; //_damage = _damage * 2;
yield return new WaitForSeconds(duration); //wait necessary amount of real time
_damage /= 2; //_damage = _damage / 2;
}
If you need to do only 5 blows with increased damage, you can use something
private float _damage = 10;
private int _increasedBlows = 0; //amount of blows with increased damage
private float _increasingCoeff = 2; //ratio of damage increase of blow
//add new increased blows after doing combo
public void AddIncreasedBlows(int amount)
{
_increasedBlows += amount;
}
//return blow damage
public float GetHitDamage()
{
if (_increasedBlows > 0)
{
_increasedBlows--;
return _damage * -_increasingCoeff; //return increased damage
}
else
{
return _damage; //return usual damage
}
}
I have a code that works just fine but i think if i keep following the same strategy to do all the same things it's gonna over load on the processor for no reason
I have a variable that represents the time and this variable rests to 0 in every frame that the gameObject has a velocity higher than 0.5f so instead of resetting it every frame i want to start the timer when it's below 0.5f
if (speed >= 0.5f)
{
t = 0;
}
t = t + Time.deltaTime;
You could use a bool value to save performance.
public static bool isTimerMoving = false;
public void Update()
{
if (speed < 0.5f)
{
t = t + Time.deltaTime;
isTimerMoving = true;
}
else if (isTimerMoving) {
t = 0;
isTimerMoving = false;
}
}
This code resets the timer whenever speed reaches 0.5f. If you only want to pause the timer, you can remove the t = 0 from it.
P.S. using > or < is faster than using <= or >=. Not by very much, but I like keeping things efficient ;)
EDIT: After asking a question, responses indicate that this statement is false, my apologies.
I desire to increment an float by 0.5 every time the user clicks the UI button and if the user presses the button for more than 2 second want to continuously increment the float by 0.5, to do so i use Event trigger (PointerDown, PointerUp) and call the functions in update. When i user the down below code i cant increment the float value continuously.
Update Code
void Update () {
transform.rotation = Quaternion.Lerp (qStart, qEnd, (Mathf.Sin(Time.time * speed) + 1.0f) / 2.0f);
if(Time.timeScale == 0)
transform.rotation = Quaternion.Euler(0,0,0);
clickCondition ();
}
PointerDown Function
public void WhenIncreaseClicked()
{
if (timeDown < 2.0f)
IncreaseBPM ();
else
increase = true;
}
PinterUp function
public void WhenIncreaseNotClicked()
{
increase = false;
Time.timeScale = 1;
}
IncreaseBPM
public void IncreaseBPM()
{
if (speed < 12)
{
speed += 0.05f;
bpmText.GetComponent<BeatTextControl> ().beats += 1;
PlayerPrefs.SetFloat ("savedBPM", speed);
}
}
ClickCondition
public void clickCondition()
{
if(increase)
{
IncreaseBPM();
}
else if(decrease)
{
DecreaseBPM();
}
}
Start
void Start () {
qStart = Quaternion.AngleAxis ( angle, Vector3.forward);
qEnd = Quaternion.AngleAxis (-angle, Vector3.forward);
timeDown = Time.deltaTime;
if (PlayerPrefs.HasKey ("savedBPM"))
speed = PlayerPrefs.GetFloat ("savedBPM");
else
speed = 1.5f;
}
I have set timeDown = Time.deltaTime in Start().
Assuming no other code alters your increase variable, you'll be unable to increment it continuously because it will never be set to true.
In Start() you have a line timeDown = Time.deltaTime;, so timeDown will be equal to the number of seconds since the last frame. Not only is this an issue because timeDown never changes (Start() is only called once), but it's an issue because it's not likely to ever be above 2.0f since a single frame is highly unlikely to take 2 seconds to complete. It will generally be a very small number, for example 0.06f.
Due to this, in your WhenIncreaseClicked() method, the if (timeDown < 2.0f) will always equate to true. Thus the else clause is never executed and increase is never set to true.
To resolve this you could create a new boolean variable, e.g. clicked and set it to true at the start of WhenIncreaseClicked() and false at the start of WhenIncreaseNotClicked(). Then in Update() you can just add Time.deltaTime to timeDown if clicked is true. You'll also want to move that if/else outside of WhenIncreaseClicked() also, and into Update(), making sure it's only run when clicked is true.
For example:
void Update () {
transform.rotation = Quaternion.Lerp (qStart, qEnd, (Mathf.Sin(Time.time * speed) + 1.0f) / 2.0f);
if(Time.timeScale == 0)
transform.rotation = Quaternion.Euler(0,0,0);
if(clicked) {
timeDown += Time.deltaTime;
if (timeDown >= 2.0f) // note the >= not <
increase = true;
}
clickCondition ();
}
and WhenIncreaseClicked():
public void WhenIncreaseClicked()
{
clicked = true;
IncreaseBPM();
}
and WhenIncreaseNotClicked():
public void WhenIncreaseNotClicked()
{
clicked = false;
increase = false;
Time.timeScale = 1;
}
You can also remove the assignment to timeDown in Start() since it's not useful.
First of all, Unity documentation says about Time.deltaTime
The time in seconds it took to complete the last frame.
When you set timeDown = Time.deltaTime timeDown becomes a tiny fraction, for example 0.01621689. Therefore, timeDown < 2.0f always returns true and your code never reaches increase = true; line.
Second point you should know is Time.time
The time at the beginning of this frame (Read Only). This is the time in seconds since the start of the game.
You used Time.time in your Update() method as Mathf.Sin(Time.time * speed). Bear in mind that Time.time can be enough large number representing the time in seconds since the start of the game as mentioned in document. When you do Time.time * speed the output number may be huge (assuming speed > 1) causing some problems. Luckily, you are using it inside Mathf.Sin() you get number only between [0, 1]. Probably, you intended to use Time.deltaTime instead.
Let's remember Time.time:
The time in seconds since the start of the game
You can use this to check time difference.
float lastDownTime;
bool isDown;
void OnPointerDown() // Method name itself says when it should be called
{
lastDownTime = Time.time;
isDown = true;
}
void OnPointerUp()
{
isDown = false;
}
void Update ()
{
if (isDown)
{
if (Time.time - lastDownTime > 2) // 2 seconds
{
IncreaseBPM ();
}
}
}
Note: Unity says about Time.timeScale:
Except for realtimeSinceStartup, timeScale affects all the time and delta time measuring variables of the Time class.
So if you insist on using Time.timeScale, instead of using Time.time you should use Time.realtimeSinceStartup in above example.
I am trying to get a background to start moving to the left faster and faster.
Was thinking of using two values one for the amount in percent to increase and one value for the interval between incrementing which would also get larger the more times an interval is hit?
public float interval = 1; // 1 second between intervals starting off
public float speed = 2; // the starting speedi
void Start () {
// move left
GetComponent<Rigidbody2D>().velocity = Vector2.left * speed;
}
void Update () {
// check if interval has been reached? How?
//if interval has been reached then ( This does not work for me..
GetComponent<Rigidbody2D>().velocity = GetComponent<Rigidbody2D>().velocity * 0.01f;
interval = interval * 2;
}
I think there's a few things you need to fix here:
1.) you are multiplying velocity by .01 every frame, this will practically freeze it completely. I'm not sure what you mean to do by that.
2.) You are calling GetComponent>Rigidbody2D<() every frame, which is very expensive! You should create a variable for it something like:
Rigidbody2d RB;
void Start () {
RB = GetComponent<Rigidbody2D>();
}
For how to increase speed after each interval, I might declare a counter which you increase every frame. And if the counter > interval then interval *= 2 and counter = 0
Well, first of all take Rigidbody2D in a variable once then play with it.
You can use Coroutine for this purpose.
public float interval = 1; // 1 second between intervals starting off
public float speed = 2; // the starting speed
Rigidbody2D _rb;
void Start () {
// move left
_rb = GetComponent<Rigidbody2D>();
_rb.velocity = Vector2.left * speed;
StartCoroutine("IncreaseSpeedWithInterval");
}
void Update () {
}
IEnumerator IncreaseSpeedWithInterval()
{
while(true){
yield return new WaitForSeconds(interval);
// Now either Multiply your velocity by 1.01f or Add by 0.01f
_rb.velocity *= 1.01f;
// ========== OR ========== //
_rb.velocity += (Vector2.one * 0.01f);
}
}