Right way to manage time event in unity - c#

I have to do some time based task(simulation) like,
As the game started:
After 2 mintues start TrainA.
After 6 mintues star Train B
after 12 minutes start Train C and so on
But remember If i want to quickly view the simulation, the timer should be speed able.
Now i am considering two approaches (other approaches are also welcome which is the purpose of the question)
Each Object (Train) script manage its time by WaitForSeconds:
void Start()
{
StartCoroutine("MyEvent");
}
private IEnumerator MyEvent()
{
yield return new WaitForSeconds(120f); // wait two minutes
//Launch Train
}
This kind of script attach to every object that requires action after a certain time:
Problem:
How do I speed up its time?
Maybe Performance intensive. As each script managing its own co-routine(Maybe I am wrong)
One Global script for Timer:
function Update ()
{
Timer += Time.deltaTime; //Time.deltaTime will increase the value with 1 every second.
if (timer>= 120){
//launch train, an so one conditions
//Or get timer variable in other script and compare time on update
}
}
Now using the above script I can get the Timer variable in another script and can execute my task based on time in the update method.
The question is how do I manage it? Either first way or second way or third way (by you)?
Because I also want to speed up the time too which seems impossible within co-routine once it registers.
Need your help folks!!

You use either of the two ways and simply change Time.timescale if you want to view it faster/slower etc in the example by pressing Space:
public class Example : MonoBehaviour
{
// Toggles the time scale between 1 (normal) and 0.5 (twice as fast)
// whenever the user hits the Space key.
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (Time.timeScale == 1.0f)
{
Time.timeScale = 0.5f;
}
else
{
Time.timeScale = 1.0f;
}
// Also adjust fixed delta time according to timescale
// The fixed delta time will now be 0.02 frames per real-time second
Time.fixedDeltaTime = 0.02f * Time.timeScale;
}
}
}
For the Coroutine this works without you having to change anything because WaitForSeconds is affected by the Time.timescale:
The actual time suspended is equal to the given time multiplied by Time.timeScale.
Time.deltaTime afaik is not affacted by the Time.timescale so in order to allow faster replay you would have to do
private void Update ()
{
Timer += Time.deltaTime * (1 / Time.timescale);
if (timer >= 120)
{
//launch train, an so one conditions
//Or get timer variable in other script and compare time on update
}
}
Whether one Update or multiple Coroutines are more performant depends a lot on the specific usecase. Afaik it isn't really noticable until you have like maybe 10.000 Coroutines running (don't nail me on numbers here ;) ).
In your case for only raising one or multiple events it might be better to stick with the one Update() method instead and invoke an event or something like that.
But - why not simply have one single Coroutine instead of the Update at all:
public class GlobalTimer : MonoBehaviour
{
public float Delay = 2.0f;
public UnityEvent onTimerDone;
// Unity allows to use Start as IEnumerator instead of a void
private IEnumerator Start()
{
yield return new WaitforSeconds(delay);
onTimerDone.Invoke();
}
}
than you have only one invoked method and can add callbacks to that timer like
globalTimerReference.onTimerDone.AddListener(() => {
Debug.LogFormat("Timer of {0} seconds is done now.", globalTimerReference.Delay);
});
or via the inspector (like in a UI.Button.onClick).
For multiple events I just came up with a quick and dirty solution so you can simply define multiple events via the inspector and add various callbacks and stuff:
public class GlobalTimer : MonoBehaviour
{
public List<UnityEvent> events;
public List<float> delays;
private void Start()
{
var validPairs = Mathf.Min(events.Count, delays.Count);
for (int i = 0; i < validPairs; i++)
{
StartCoroutine(InvokeDelayed(events[i], delays[i]));
}
}
private IEnumerator InvokeDelayed(UnityEvent unityEvent, float delay)
{
yield return new WaitForSeconds(delay);
unityEvent.Invoke();
}
}
Just make sure that for every event there is a delay in the list. In the future you might want to write a proper CustomEditor in order to edit this more beautiful in the inspector.
Update
or you can take mine :D
public class ExampleScript : MonoBehaviour
{
[SerializeField] private List<EventDelayPair> EventDelayPairs;
private void Start()
{
foreach (var eventDelayPair in EventDelayPairs)
{
StartCoroutine(InvokeDelayed(eventDelayPair.unityEvent, eventDelayPair.Delay));
}
}
private IEnumerator InvokeDelayed(UnityEvent unityEvent, float delay)
{
yield return new WaitForSeconds(delay);
unityEvent.Invoke();
}
[Serializable]
private class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
}
}
[CustomEditor(typeof(ExampleScript))]
public class ExampleInspector : Editor
{
private SerializedProperty EventDelayPairs;
private ReorderableList list;
private ExampleScript _exampleScript;
private void OnEnable()
{
_exampleScript = (ExampleScript)target;
EventDelayPairs = serializedObject.FindProperty("EventDelayPairs");
list = new ReorderableList(serializedObject, EventDelayPairs)
{
draggable = true,
displayAdd = true,
displayRemove = true,
drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, "DelayedEvents");
},
drawElementCallback = (rect, index, sel, act) =>
{
var element = EventDelayPairs.GetArrayElementAtIndex(index);
var unityEvent = element.FindPropertyRelative("unityEvent");
var delay = element.FindPropertyRelative("Delay");
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), delay);
rect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUI.GetPropertyHeight(unityEvent)), unityEvent);
},
elementHeightCallback = index =>
{
var element = EventDelayPairs.GetArrayElementAtIndex(index);
var unityEvent = element.FindPropertyRelative("unityEvent");
var height = EditorGUI.GetPropertyHeight(unityEvent) + EditorGUIUtility.singleLineHeight;
return height;
}
};
}
public override void OnInspectorGUI()
{
DrawScriptField();
serializedObject.Update();
list.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
private void DrawScriptField()
{
// Disable editing
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour(_exampleScript), typeof(ExampleScript), false);
EditorGUI.EndDisabledGroup();
EditorGUILayout.Space();
}
}
Example
or with a preview of the delays for debugging
public class ExampleScript : MonoBehaviour
{
public List<EventDelayPair> EventDelayPairs;
private void Start()
{
foreach (var eventDelayPair in EventDelayPairs)
{
StartCoroutine(InvokeDelayed(eventDelayPair));
}
}
private IEnumerator InvokeDelayed(EventDelayPair pair)
{
var timer = pair.Delay;
do
{
timer -= Time.deltaTime * (1 / Time.timeScale);
pair.Delay = timer;
yield return null;
} while (timer > 0);
pair.Delay = 0;
pair.unityEvent.Invoke();
}
[Serializable]
public class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
}
}
Btw your comment
//Time.deltaTime will increase the value with 1 every second.
is not formulated correctly. Instead it should be:
Time.deltaTime will increase the value every frame about the time passed in seconds since the last frame was rendered.
EDIT - Reduce delays afterwards
I understood from the question that you wanted to speed up the entire playback.
From the comments I learned now that instead you wanted to rather reduce the delay afterwards. So you can't use Time.timescale.
For this you can use the second example a bit altered:
[Serializable]
public class EventDelayPair
{
public UnityEvent unityEvent;
public float Delay;
// add a time multiplicator for each delay with default 1
public float TimeMultiplicator = 1.0f;
}
Note: You'll have to also add it to the EditorScript if you use it - I leave this as your homework ;)
private IEnumerator InvokeDelayed(EventDelayPair pair)
{
var timer = pair.Delay;
do
{
timer -= Time.deltaTime * pair.TimeMultiplicator;
pair.Delay = timer;
yield return null;
} while (timer > 0);
pair.Delay = 0;
pair.unityEvent.Invoke();
}
so you can in the Inspector or also by script
exampleScriptReference.EventDelayPairs[0].TimeMultiplicator = 2;
reduce the Delay faster.
You might also want to add an overall multiplicator like
// Again you have to add it to the inspector if you use it
public float overallMultiplicator = 1.0f;
//...
timer -= Time.deltaTime * pair.TimeMultiplicator * overallMultiplicator;

Related

This script to increase the Focal Distance of my Depth Of Field override using URP in unity causes unity to freeze with no errors

I tried to create this script which slowly increases the focal distance when a button is pressed(running on start for now) but it causes my unity to freeze with no errors and only way to restart unity is by forcing it to close through task manager.
`
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Performance.ProfileAnalyzer;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class UnblurBackground : MonoBehaviour
{
public Volume vol;
public float fadeRate = 0.01f;
public float startFocusDistance = 0.1f;
private float focusDistance = 0.1f;
DepthOfField dph;
void Awake()
{
//Borrowed from unity forums. Gets the volume component and checks for Depth of Field override.
VolumeProfile volumeProfile = GetComponent<Volume>()?.profile;
if (!volumeProfile) throw new System.NullReferenceException(nameof(VolumeProfile));
if (!volumeProfile.TryGet(out dph)) throw new System.NullReferenceException(nameof(dph));
//
//Sets Depth of Field
dph.focusDistance.Override(0.1f);
focusDistance = startFocusDistance;
}
public void unblur()
{
while(focusDistance <= 10)
{
//Increases 'focusDistance' variable over time.
focusDistance += (fadeRate / 1000);
//Sets depth of field to the 'focusDistance' variable.
dph.focusDistance.Override(focusDistance);
}
}
void Start()
{
unblur();
}
}
`
I have found that if I do not divide the fade rate by 10 it does not crash but it is either fading so quick I can't see it or is simply jumping to 10 instantly.
May be a noob question but I appreciate any assistance. :)
Thanks,
Braith
First of all
VolumeProfile volumeProfile = GetComponent<Volume>()?.profile;
should NOT be used! See Why does C# null-conditional operator not work with Unity serializable variables?
Rather use e.g.
if(!TryGetComponent<Volume>(out var volumeComponent))
{
Debug.LogError($"No {nameof(Volume)} component attached to this object!", this);
return;
}
var volumeProfile = volume.profile;
Then in general you do not want any long running while loops.
//Increases 'focusDistance' variable over time.
No it doesn't!
Currently it is trying to run the entire while loop in one single frame .. until it is done it freezes.
Currently you need 1 / 0.01 / 1000 / 10 = 1000000 iterations to finish the loop.
I assume what you rather would want to do is a smooth transition using a fixed duration. One of the main tools to achieve this in Unity are Coroutines like e.g.
public float durationInSeconds = 1f;
public float startFocusDistance = 0.1f;
public float targetFocusDistance = 10f;
// If you do this then Start is automatically run as a Coroutine
IEnumerator Start()
{
for(var timePassed = 0f; timePassed < durationInSeconds; timePassed += Time.deltaTime)
{
var factor = timePassed / durationInSeconds;
dph.focusDistance.Override(Mathf.Lerp(startFocusDistance, targetFocusDistance, factor));
// basically means "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
}

Unity : How do I change a Shader Graph's property (float) over time?

As seen below, I created a shader effect and added a Vector1 property called "Visibility", where if set to 1 nothing happens but as it progresses to 0 the shader effect comes into play and becomes visible.
enter image description here
enter image description here
My issue is finding a way to reduce the Visibility property from 1 to 0 over time, in the span of 2 seconds it should go from 1 to 0. I know how to change the value of the "Visibility" property through code using SetFloat, like this:
if (door.GetComponent<MeshRenderer>().material = changeDisolve)
{
changeDisolve.SetFloat("Visibility", 0.0f);
}
But i want the value to progress to 0 via a 'duration', so i tried using Mathf.Lerp as seen below-which did not work either:
if (door.GetComponent<MeshRenderer>().material = changeDisolve)
{
float changeVisibility = Mathf.Lerp(1f, 0f, 3f);
changeDisolve.SetFloat("Visibility", changeVisibility);
}
Does anyone know what i am doing wrong or how to go about this? Thanks in advance.
If you want to change the code to be called each X Seconds you should use InvokeRepeating. You could also start InvokeRepeating the moment you enter a BoxCollider with OnTriggerEnter() or the moment you leave a BoxCollider with OnTriggerExit() etc, but you can't use Update.
You can use CancelInvoke(); to stop all InvokeRepeating calls. In this case after your shaderValue has reached 0 or less.
// Starting at the start of the game after Awake()
// the shader will be changed every 0.2 seconds
public class ExampleScript : MonoBehaviour
{
private shaderValue = 1f;
private float change = 0.1f;
void Start()
{
InvokeRepeating("ChangeShader", 0.1f, 0.2f);
}
void Changeshader()
{
if (shaderValue <= 0f) {
CancelInvoke();
}
else if (door.GetComponent<MeshRenderer>().material = changeDisolve) {
shaderValue -= change;
changeDisolve.SetFloat("Visibility", shaderValue);
}
}
}
You could also use an IEnumerator, which makes it possible to call WaitForSeconds which will then wait that amount and after the specified delay has been finished continue with the rest of the code.
You could also use OnTriggerEnter() or OnTriggerExit() instead of Start().
// Starting in Update
// the shader will be changed every 0.2 seconds
public class ExampleScript : MonoBehaviour
{
private shaderValue = 1f;
private float change = 0.1f;
private float delay = 0.2f;
void Start()
{
StartCouroutine(ChangeShader());
}
IEnumerator Changeshader()
{
while (shaderValue > 0f){
yield return new WaitForSeconds(delay);
if (door.GetComponent<MeshRenderer>().material = changeDisolve) {
shaderValue -= change;
changeDisolve.SetFloat("Visibility", shaderValue);
}
}
}
}

Adjust the length of animation clip to match cooldown

I'm planning on have several buttons to spawn monsters, all with a different cooldown. Is there a way to match the animation clip to be the same length as the cooldown of the button?
Here is an example: https://gyazo.com/0a2ae868e5458c701e1a258aac6dc59a
The animation is 1 second but the cooldown is 3 seconds.
Here is my code:
private void ButtonCooldown()
{
if (GetComponent<Button>().interactable == false)
{
buttonTimer += Time.deltaTime;
if (buttonTimer >= cooldown)
{
GetComponent<Button>().interactable = true;
buttonTimer = 0;
}
}
}
public void DisableButton()
{
GetComponent<Button>().interactable = false;
myAnimatior.SetTrigger("ButtonCooldownAnimation");
}
You could adjust the according Animator's speed to adjust it's overall playback speed.
E.g. something like
// Adjust in the inspector
[SerializeField] private float cooldownTime = 3;
// Already reference this via the Inspector if possible
[SerializeField] private Button button;
private void Awake ()
{
// Do this only once!
if(!button) button = GetComponemt<Button>();
}
public void DisableButton()
{
button.interactable = false;
// typo in Animator btw ;)
myAnimatior.SetTrigger("ButtonCooldownAnimation");
// Make the Animator play slower so the animation now takes 3 seconds
myAnimatior.speed = 1/cooldownTime;
// Instead of Update simply use Invoke here
// Execute the method called WhenCooldownDone after cooldownTime seconds
Invoke(nameof(WhenCooldownDone), cooldownTime);
}
private void WhenCooldownDone ()
{
button.interactable = true;
myAnimator.speed = 1;
}
As in the comments I would use Invoke instead of constantly checking the states in Update. In particular never use GetComponentrepeatedly in Update. It is very expensive. Always try to rather store the reference and reuse it.

Is there a way to start a method from button click without using Update() function in unity

Below is my C# script. I added a button to my project with a On Click event and called the Rotate() method. But for some reason it is not working
using System.Threading;
using UnityEngine;
public class Orbit : MonoBehaviour {
public GameObject sun;
public float speed;
// Use this for initialization
void Start () {
}
public void Update()
{
Rotate();
}
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
}
I commented the Update() method when calling the Rotate() method. I also created a game object for the script.
The reason why it only works in Update currently is that
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
needs to be called repeatetly. Otherwise it will only rotate for exactly one frame and cause of Time.deltaTime only a very small amount. But the onClick event of the Button component is fired only once. It is similar to e.g. Input.GetKeyDown which is only called once when the key goes down. There is no implementation in the Button component itslef to handle a continued button press.
What you want instead as far as I understand is rotating the object after the button click
start to rotate for ever
for a certain duration
until you press the button again
until it is released (-> implement a continuesly firing button see below)
The Button component alone can only do the first three:
Rotate for ever
Either using a Coroutine
private bool isRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isRotating) return;
// start the rotation
StartCoroutine(RotateRoutine());
isRotating = true;
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or still doing it in Update
private bool isRotating = false;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void Rotate()
{
// enable the rotation
isRotating = true;
}
Note that the Update solution is only for your understanding what is happening. It should not be used like that because it is not that efficient since Update is called continously and checks the bool also if not rotating yet. That produces unnecessary overhead. The same applies to all following examples: Prefere to use the Coroutines over Update (In this case! In other cases it is actuall better and more efficient to use one Update method instead of multiple concurrent Coroutines .. but that's another story.)
Rotate for a certain duration
As Coroutine
// adjust in the inspector
// how long should rotation carry on (in seconds)?
public float duration = 1;
private bool isAlreadyRotating;
public void Rotate()
{
// if aready rotating do nothing
if(isAlreadyRotating) return;
// start a rottaion
StartCoroutine(RotateRoutine());
}
private IEnumerator RotateRoutine()
{
// set the flag to prevent multiple callse
isAlreadyRotating = true;
float timePassed = 0.0f;
while(timePassed < duration)
{
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// add the time passed since last frame
timePassed += Time.deltaTime;
// leave here, render the frame and continue in the next frame
yield return null;
}
// reset the flag so another rotation might be started again
isAlreadyRotating = false;
}
or in Update
public float duration;
private bool isRotating;
private float timer;
private void Update()
{
// if not rotating do nothing
if(!isRotating) return;
// reduce the timer by passed time since last frame
timer -= Time.deltaTime;
// rotate a small amount
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// if the timer is not 0 return
if(timer > 0) return;
// stop rottaing
isRotating = false;
}
public void Rotate()
{
// if already rotating do nothing
if(isRotating) return;
// start rotating
isRotating = true;
// enable timer
timer = duration;
}
Toggle rotation
This is very similar to the one before but this time instead of the timer you stop the rotation by clicking again. (You even could combine the two but than be carefull to reset the isRotating flag correctly ;) )
As Coroutine
private bool isRotating;
public void ToggleRotation()
{
// if rotating stop the routine otherwise start one
if(isRotating)
{
StopCoroutine(RotateRoutine());
isRotating = false;
}
else
{
StartCoroutine(RotateRoutine());
isRotating = true;
}
}
private IEnumerator RotateRoutine()
{
// whuut?!
// Don't worry coroutines work a bit different
// the yield return handles that .. never forget it though ;)
while(true)
{
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
// leave here, render the frame and continue in the next frame
yield return null;
}
}
or as Update
private bool isRotating;
private void Update()
{
// if not rotating do nothing
if(!isRottaing) return;
// rotate a bit
transform.RotateAround(sun.transform.position, Vector3.up, speed * Time.deltaTime);
}
public void ToggleRotation()
{
// toggle the flag
isRotating = !isRotating;
}
Rotate until released
This is the most "complicated" part since the Button alone can not accomplish this (there is no "on Release"). But you can implement this using IPointerXHandler interfaces.
The good news: You can keep your original script as you have it currently
public void Rotate()
{
transform.RotateAround(sun.transform.position, Vector3.up, speed *
Time.deltaTime);
}
Now you need an extension for the button. It will call the whilePressed event repeatedly every frame like Update so you just have to reference your Rotate method in whilePressed instead of the onClick.
Again there are two options either implementing it as a Coroutine:
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
// reference the same way as in onClick
public UnityEvent whilePressed;
private Button button;
private bool isPressed;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
// Handle pointer down
public void OnPointerDown()
{
// skip if the button is not interactable
if(!button.enabled || !button.interactable) return;
// skip if already rotating
if(isPressed) return;
StartCoroutine(PressedRoutine());
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
isPressed= false;
}
private IEnumerator RotateRoutine()
{
// repeatedly call whilePressed until button isPressed turns false
while(isPressed)
{
// break the routine if button was disabled meanwhile
if(!button.enabled || !button.interactable)
{
isPressed = false;
yield break;
}
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
// leave here, render the frame and continue in the next frame
yield return null;
}
}
}
or you could do the same in Update again as well
[RequireComponent(typeof(Button))]
public class HoldableButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler
{
public UnityEvent whilePressed;
private bool isPressed;
private Button button;
private void Awake()
{
button = GetComponent<Button>();
if(!button)
{
Debug.LogError("Oh no no Button component on this object :O",this);
}
}
private void Update()
{
// if button is not interactable do nothing
if(!button.enabled || !button.interactable) return;
// if not rotating do nothing
if(!isPressed) return;
// call whatever is referenced in whilePressed;
whilePressed.Invoke();
}
// Handle pointer down
public void OnPointerDown()
{
// enable pressed
isPressed= true;
}
// Handle pointer up
public void OnPointerUp()
{
// disable pressed
isPressed= false;
}
// Handle pointer exit
public void OnPointerExit()
{
// disable pressed
isPressed= false;
}
}
Place this component next to a Button component. You don't have to reference anything in onClick just leave it empty. Instead reference something in onPressed. Keep the Button component though since it handles also the UI style for us (like hover changes the color/sprite etc.)
Again: The Update solutions might look cleaner/simplier for now but are not as efficient (in this usecase) and easy to controll (this might be opinion based) as the Coroutine solutions.
Please search on the article regarding the key press functionalities . It would help you a lot in finding your answer. Update is used if we need to do something continuously in our project where as the key pressed is used when we have do it for once
this example is also being used to resolve your issue and use this Script when a specific button is is being pressed

How to make a half second cooldown for weapon attacks in a video game in c#

I need to make a cooldown for an attack. Right now you can spam it and can swing as fast as you can click but I would like to balance the game better and add a small cooldown. I cant seem to find how to do this anywhere though. Anyone have any ideas?
You can use IEnumerator function which contains something like this:
public class YourClass : MonoBehaviour
{
private float cooldownTime;
private bool isCooldown;
// your code
private void Update()
{
if (!isCooldown)
{
// Do stuff
}
}
private IEnumerator Cooldown()
{
// Start cooldown
isCooldown = true;
// Wait for time you want
yield return new WaitForSeconds(cooldownTime);
// Stop cooldown
isCooldown = false;
}
}
Just start coroutine in attack handler
You would want something along the lines of the following. Since I don't have any of your code, I'll assume this is under a player class and will write accordingly.
class PlayerClass : MonoBehaviour
{
float timer = 0.0f;
float cooldownTime = 1.0f;
void Update() {
if(timer > cooldownTime) {
if(Input.GetMouseButtonDown(0)) {
Attack();
timer = 0;
}
}
if(timer < cooldownTime + 1) // Add some leaniency for inaccurate floating points.
timer += time.DeltaTime;
}
}
Obviously, try this code. I'm at work right now and don't have access to my PC so you'll need to deal with untested C#! And of course, do some googling on timers and stuff like that, because that will be where you need to look. Also look into IEnumerable for asynchronous code if you're looking to get fancy.
(>^3^)>

Categories

Resources