I'm trying to make countdown timer for adding my game match by using custom properties. But i'm stuck somewhere. Timer is working but it's not shows same time with another clients who join later. My script here:
public float Totaltime = 600;
void Update()
{
Totaltime -= Time.deltaTime;
StartCountDownTimer(Totaltime);
}
void StartCountDownTimer(float totalSeconds)
{
Hashtable ht = new Hashtable() { { "startTime", totalSeconds } };
PhotonNetwork.room.SetCustomProperties(ht);
float updatedSecond = (float)PhotonNetwork.room.CustomProperties["startTime"];
int minutes = Mathf.FloorToInt(updatedSecond / 60f);
int seconds = Mathf.RoundToInt(updatedSecond % 60f);
string formatedSeconds = seconds.ToString();
if (seconds == 60)
{
seconds = 0;
minutes += 1;
}
}
There is an issue in the logic of code. You should only set the start time on the master client. Other clients read the start time and calculate the remaining time in the local client. After the countdown ends, the master client sends an RPC call to notify other clients to do next step.
Related
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 game where I have time to give energy. My timer works fine when game is active but when game is inactive the time pauses and after I start game again the timer starts from wher it was left.
I want keep timer going even if the game is inactive and add energy.
using System;
using UnityEngine;
using UnityEngine.UI;
public class EnergyAdder : MonoBehaviour
{
// Start is called before the first frame update
public float waitTime = 600;
Timer timer;
public GameRoot gameRoot;
void Awake()
{
//timer = new Timer(waitTime);
float remainingTime = PlayerPrefs.GetFloat("TimeOnExit");
timer = new Timer(remainingTime);
}
// Update is called once per frame
void Update()
{
if (gameRoot.energyCap >= 5)
{
GetComponent<Text>().text = "Full";
timer.refresh();
}
else
{
timer.countDown();
if (timer.isFinished())
{
timer = new Timer(waitTime);
timer.refresh();
gameRoot.AddEnergy(1);
}
UpdateClock();
}
}
void UpdateClock()
{
int seconds = ((int)timer.timeLeft) % 60;
int minutes = (int)(timer.timeLeft / 60);
GetComponent<Text>().text = minutes.ToString() + ":" + seconds.ToString("00");
}
void OnApplicationQuit()
{
PlayerPrefs.SetFloat("TimeOnExit", timer.timeLeft);
}
}
Please help I'm new to unity and c#.
What you are doing is saving time value when you left, and load that value when you start the game again.
Maybe you should (when start the game) compare that loaded time with the current time, the difference will be the time elapsed.
I have created a maze game with 3 health hearts and a timer countdown. when the timer reaches zero, it is supposed to subtract one heart and give additional 30 seconds before subtracting another one. when the player is on the last heart and the timer reaches zero, the game will be over.
My problem is that when the timer reaches zero, it erases all my health bars and the game ends. Another was that the timer countdown grants the additional 30 seconds and all the hearts are gone and the timer does not show the 30 seconds on the text and the game does not end.
I created two scripts, one for health called LifeHeart.cs and one for timer called Loading.cs. I am new to unity so I implemented these codes from various sources and made them my own. The loading.cs shows that when the timer seconds is 0 and the timer minutes is 0, it is supposed to minus one heart and add an additional 30 seconds and start the coroutine. In the Lifeheart.cs there is a switch case showing when there is one heart subtracted, it is supposed to remove one heart by setting it to false.
I researched and I found that others have said that the static variable is the reason that all the health hearts gets erased, so I changed the way to call the variable to gameobject.find, but that still doesn't work. to make the timer countdown to grant the additional 30 seconds, I commented the "Time.timeScale = 0;" in the case 0 in LifeHeart.cs, it grants the 30 seconds but the hearts are all wiped out and the text on the timer does not show the 30 second countdown.
This is the first script of the code from my LifeHeart.cs- the comments are the ways i was trying to make it work
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LifeHeart : MonoBehaviour
{
public GameObject heart1;
public GameObject heart2;
public GameObject heart3;
public static int health=3;
// Start is called before the first frame update
void Start()
{
health = 3;
heart1.gameObject.SetActive(true);
heart2.gameObject.SetActive(true);
heart3.gameObject.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (health > 3)
health = 3;
switch (health)
{
case 3:
heart1.gameObject.SetActive(true);
heart2.gameObject.SetActive(true);
heart3.gameObject.SetActive(true);
break;
case 2:
heart1.gameObject.SetActive(true);
heart2.gameObject.SetActive(true);
heart3.gameObject.SetActive(false);
break;
case 1:
heart1.gameObject.SetActive(true);
heart2.gameObject.SetActive(false);
heart3.gameObject.SetActive(false);
break;
case 0:
heart1.gameObject.SetActive(false);
heart2.gameObject.SetActive(false);
heart3.gameObject.SetActive(false);
//Time.timeScale = 0; //to make the extra 30 sec to work
break;
}
}
}
//---------------------------------------------------------------------
//and this is second script of the code from my timer called Loading.cs
//---------------------------------------------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Loading : MonoBehaviour
{
public Image loading;
public Text timeText;
public int minutes;
public int sec;
int totalSeconds = 0;
int TOTAL_SECONDS = 0;
float fillamount;
void Start()
{
timeText.text = minutes + " : " + sec;
if (minutes > 0)
totalSeconds += minutes * 60;
if (sec > 0)
totalSeconds += sec;
TOTAL_SECONDS = totalSeconds;
StartCoroutine(second());
}
void Update()
{
//GameObject gameControl = GameObject.Find("GameControl");//name of my gameobject
//LifeHeart LifeHeart = gameControl.GetComponent<LifeHeart>();
if (sec == 0 && minutes == 0 && LifeHeart.health>0)
{
LifeHeart.health -= 1;
totalSeconds += sec + 30;
TOTAL_SECONDS = totalSeconds;
StartCoroutine(second());
}
else if (sec == 0 && minutes == 0 && LifeHeart.health == 0)
{
timeText.text = "Time's Up!";//this one activates instead of showing the 30 seconds on the timer
//Time.timeScale = 0;
StopCoroutine(second());
}
}
IEnumerator second()
{
yield return new WaitForSeconds(1f);
if (sec > 0)
sec--;
if (sec == 0 && minutes != 0)
{
sec = 60;
minutes--;
}
timeText.text = minutes + " : " + sec;
fillLoading();
StartCoroutine(second());
}
void fillLoading() // this is the code for the image of the timer clock
{
totalSeconds--;
float fill = (float)totalSeconds / TOTAL_SECONDS;
loading.fillAmount = fill;
}
}
1st problem: all health hearts disappear and game stops
2nd problem: all health hearts disappear, but timer grants 30 seconds but does not show on timer text
I am sorry if I have made any mistakes. Your help is greatly appreciated
You have a lot of "seconds" variables.
if (sec == 0 && minutes == 0 && LifeHeart.health>0)
{
LifeHeart.health -= 1;
totalSeconds += sec + 30;
TOTAL_SECONDS = totalSeconds;
StartCoroutine(second());
}
If sec is 0, you lose a life, and add 30 to totalSeconds- but I don't see where you ever add 30 to sec. So the next update, sec will still be zero- and you'll lose another life. You lose a heart every Update until you're dead.
Quick fix would be to add 30 to sec at the same time you add it to totalSeconds.
Better might be to see if your really need sec, totalSeconds, and TOTAL_SECONDS- can you combine these three into a single variable? It seems like they should have the same value, and the problem here is because they don't?
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
}
I'm trying to display 3 times at once for a turn based game.
The first time is total time being counted down.
The other two timers shows countdown time for each player.
I also have a boolean value being checked from another class to see which turn it is(myplayerturn)
Right now I can show total time and player time all counting down effectively, but when one player time is running i want to pause the other player timer. So far my code is as follows
public class countDown : MonoBehaviour {
public Text timerText;
public Text PlayerTime;
public Text OpponentTime;
public float myTimer = 3600;
// Use this for initialization
void Start () {
timerText = GetComponent<Text>();
PlayerTime = GetComponent<Text>();
OpponentTime = GetComponent<Text>();
}
// Update is called once per frame
void Update () {
if (GetComponent<differentclass>().myplayerturn)
{
//I'm gueussing this is where i need to pause OpponentTime;
// and start PlayerTime
}
else{
// pause PlayerTime
}
int minutes = Mathf.FloorToInt(myTimer / 60F);
int seconds = Mathf.FloorToInt(myTimer - minutes * 60);
string rTime = string.Format("{0:00}:{1:00}", minutes, seconds);
myTimer -= Time.deltaTime;
timerText.text = rTime;
PlayerTime.text = rTime;
OpponentTime.text = rTime;
}
}
I would store minutes and seconds in a small class and give every player a class and when its paused just dont increment the time.
That would look like this:
public class PlayerTime {
public int seconds = 0;
public int minutes = 0;
public float myTimer = 3600;
}
Then inside of the update I would just use the persons class variable.