I'm making a project with Augmented Reality, using Unity and Vuforia extensions. I'm new to C#, but I was looking for a method similar to ARToolKit's getFrame(), and I'm really not finding anything.
My questions are:
Is it necessary that I can calculate the frame-rate that my scene is operating at?
Which scene object should i use to track the frame-rate?
Thats as simple as:
public float avgFrameRate;
public void Update()
{
avgFrameRate = Time.frameCount / Time.time;
}
Put this code in any MonoBehaviour and attatch it to any GameObject in the scene hierarchy.
Please note: this will only give you an average frame-rate. For a more current frame-rate, other answers have addressed effective ways of accomplishing that.
None of the answers here consider the fact that the timescale can be modified in Unity and if it is, all the above approaches will be incorrect. This is because Time.Delta time is influenced by the timescale.
As such, you need to use Time.unscaledDeltaTime:
int fps = 0;
void Update () {
fps = (int)(1f / Time.unscaledDeltaTime);
}
You should look at Time.smoothDeltaTime. This returns a smoothed Time.deltaTime value which you can use instead of having to smooth it yourself using one of the techniques mentioned in other answers.
You will want something like a timer that tracks the time, and how long it took to update the screen, and extrapolates from that how many frames are drawn in a second.
I am fairly rusty with Unity, but I believe something like 1/Time.deltaTime should give you what you want.
So you'd have something like
public void Update()
{
framerateThisFrame = 1/Time.deltaTime;
}
Next you would have to decide how often to change the displayed FPS, since framerateThisFrame can change a lot during every frame. You might want to change it every two seconds for example.
EDIT
An improvement you might want to make is something like storing the past n frames, and use an average to calculate the FPS, then display it. So you could end up with something like:
public int Granularity = 5; // how many frames to wait until you re-calculate the FPS
List<double> times;
int Counter = 5;
public void Start ()
{
times = new List<double>();
}
public void Update ()
{
if (counter <= 0)
{
CalcFPS ();
counter = Granularity;
}
times.Add (Time.deltaTime);
counter--;
}
public void CalcFPS ()
{
double sum = 0;
foreach (double F in times)
{
sum += F;
}
double average = sum / times.Count;
double fps = 1/average;
// update a GUIText or something
}
EDIT
You might even multiply the frame time by Time.timeScale, if you want to be consistent while you apply slow-down/time altering effects.
Since the framerate can vary constantly, it will change many times during a given second. I've used the following recommended approach to get the current framerate. Just put it in a new script and add it to a new, empty game object in your scene.
float deltaTime = 0f;
void Update() {
deltaTime += (Time.deltaTime - deltaTime) * .1f;
}
Source, including display method: http://wiki.unity3d.com/index.php?title=FramesPerSecond
IEnumerator FramesPerSecond()
{
while (true)
{
yield return new WaitForSeconds(1);
Debug.LogFormat("Fps {0}", Time.frameCount/Time.time);
}
}
private void Start()
{
StartCoroutine(FramesPerSecond());
}
Related
I am coding a stamina bar in my game and have had it all hooked up, though the tutorial I followed for it was specifyed for a burst of stamina used. I was hoping I would be able to slowly deminish it until there is none left. I tried to apply a while loop hooked up to a coroutine though it freezes unity. I just want way to lose stamina consistantly while a button is held. Its unable to be put under the void update because the code to check if shift is activated only plays it once. I activate the script by using "stamina.instance.UseStamina(5);". Heres the code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class stamina : MonoBehaviour
{
public Slider staminabar;
private int maxstamina = 100;
private int currentstamina;
public static stamina instance;
private WaitForSeconds regenTick = new WaitForSeconds(0.1f);
private Coroutine regen;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
currentstamina = maxstamina;
staminabar.maxValue = maxstamina;
staminabar.value = maxstamina;
}
// Update is called once per frame
void Update()
{
}
public void UseStamina(int amount)
{
if (currentstamina - amount >= 0)
{
currentstamina -= amount;
staminabar.value = currentstamina;
if (regen != null)
StopCoroutine(regen);
regen = StartCoroutine(regenstamina());
}
else
{
Debug.Log("out of stamina!");
}
}
private IEnumerator regenstamina()
{
yield return new WaitForSeconds(2);
while (currentstamina < maxstamina)
{
currentstamina += maxstamina / 100;
staminabar.value = currentstamina;
yield return regenTick;
}
regen = null;
}
}
I was expecting with a while loop for a smooth way for the slider to go down instead it just froze with me needing to go into taskmanager to close unity. I also tried putting a small wait for seconds hoping that it was a issue with lag.
If I'm not mistaken, there's a couple of issues that I see.
Firstly, your stamina bar value should be a value between 0f and 1f. But your code has set a maxValue, which I'm not sure where that's coming from. So maybe you're using a different stamina bar? Not sure. But the default Slider uses a value between 0f and 1f.
Moving on to the second issue. You're doing divisions on integers. If you use any stamina, and your current stamina is less that the max stamina (which it almost always will be), then your code is always going to get stuck. For example, you use 1 stamina, then your calculation is 99 / 100 = 0. YHour current stamina will never be increased. That's just the way integer calculations work.
I also noted your UseStamina method reduces the stamina by the correct amount, but then your co-routine is trying to do modify the current stamina again. It's doing the caluclations twice, and only the first is the correct one.
You can cast to floats first, then back to an int. Or you could use a different measurement. For example:
public bool UseStamina ( int amount )
{
if ( currentstamina - amount <= 0 )
{
Debug.Log ( "No enough stamina!" );
return false;
}
if ( regen != null )
StopCoroutine ( regen );
currentstamina -= amount;
regen = StartCoroutine ( regenstamina ( ) );
return true;
}
private IEnumerator regenstamina ( )
{
yield return new WaitForSeconds ( 2 );
var timer = 0f;
while ( timer < 1f )
{
timer += Time.deltaTime;
staminabar.value = Mathf.Lerp ( staminabar.value, ( float ) currentstamina / maxstamina, timer );
yield return null;
}
regen = null;
}
Not that using Lerp like this is often seen as an error. You generally want set starting and ending points, but by having the starting point being updated to the current value, you end up with a bit of an 'easing' effect. Play around with that if the effect isn't what you're looking for.
Also note, the code was written but not tested... but, I stand by my previous comments.
I just want way to lose stamina consistantly while a button is held
I had this exact problem, when I started Unity development two years ago.
Perhaps, as stated by someone, this answer might not directly help OP. But, the general community of stackoverflow might benefit from this answer. I'm sharing this because it's the most readable and performant solution I'm currently using. If you have other approaches or better solutions readability/performance wise, please let me know.
We should refrain from using coroutines due to reasons stated in Why Rx?:
Using Coroutine is not good practice for asynchronous operations for the following (and other) reasons:
Coroutines can't return any values, since its return type must be IEnumerator.
Coroutines can't handle exceptions, because yield return statements cannot be surrounded with a try-catch construction.
Also stated that:
UniRx helps UI programming with uGUI. All UI events (clicked, valuechanged, etc) can be converted to UniRx event streams. And streams are cheap and everywhere.
You can achieve on hold operation without coroutine if you use UniRx. For example:
button.OnPointerDownAsObservable()
.Throttle(TimeSpan.FromSeconds(0.4f)) // How many seconds to hold to trigger
.SelectMany(_ => Observable.Interval(TimeSpan.FromSeconds(0.04f))) // Diminishing speed
.TakeUntil(button.OnPointerUpAsObservable()) // Execute until button is released
.RepeatUntilDestroy(button)
.Subscribe(_ =>
{
if ((currentstamina - amount) > 0)
{
currentstamina -= amount;
staminabar.value = currentstamina;
}
else{
currentstamina = 0;
staminabar.value = currentstamina;
}
});
The above code might not meet your exact needs if you just copy-paste. It's just an example but I really hope it gives you a general idea and knowledge about other ways of achieving what you're looking for.
I'm making a game in which I want to player collect coins using a magnet
here is the coins spawning code
// Spawn Coins In Air
void SpawnCoinsInAir ()
{
float yPos = transform.position.y + 1f;
float zPos = transform.position.z + 4f;
for (int i = 0 ; i < 10 ; i += 1)
{
int line = i == 0 ?
RandomsPlayerController.Instance.CurrentLineIdx
:
Random.Range(0, RandomsPlayerController.Instance.Lines.Count);
for (int j = 0 ; j < Random.Range(3,6) ; j += 1)
{
zPos += j + 10f;
var it = Instantiate(
coinTransform,
new Vector3(RandomsPlayerController.Instance.Lines [line].x,yPos , zPos),
Quaternion.identity
);
// do random rotattion in y
it.transform.DORotate (
Utilities.VectorY (Vector3.zero, Random.Range(-360,360)),
1f,
RotateMode.FastBeyond360
)
.SetEase (Ease.InOutSine);
Destroy (it, actionDuration + 1f);
}
}
}
Now I got all my coins in my game and I have to find each coin by its tag and collect them in the shortest distance.
I'm wondering how much cost it takes to find all coins in the Update function OR is there any way to do the same thing by keeping performance in mind?
Here is the code
private void UseMagnet ()
{
// collect coins
foreach (var coin in GameObject.FindGameObjectsWithTag ("coin")) continue;
}
private void Update () => UseMagnet();
Thanks in Advance
For smaller games, the cost is insignificant to the player. But it tends to get exponential the larger the search-space it.
How much cost GameObject.FindGameObjects
This is a case-by-case answer, use the profiler to see what is causing the most lag in your game.
Though unity documentation did state For performance reasons, it is recommended to not use this function every frame.
OR is there any way to do the same thing by keeping performance in mind?
Yes, what you are looking for is called caching.Create a static list and store all the coins there, like so:
// NOTE: Alternatively, you can turn this into a singleton
public static class GlobalCache {
// Transform or Coin Object.
private static HashSet<Transform> coinCache = new HashSet<Transform>();
public static Transform FetchAnyCoin() {
if (coinCache.Count <= 0) {
// Create a new coin, return it;
// NOTE: Ideally, creation of coins into this cache should be done else-where.
// The 'cache' should only handle storing and get/set requests.
return CreateNewCoin();
}
var result = coinCache.First();
// You can remove the fetched coin from the cache if you like.
coinCache.Remove(result);
return result;
}
}
In contrary to the generic Find, which you should never use if there is any other option, the FindGameObjectsWithTag as the name says uses a hashed (pre-indexed) tag which is quite optimized and not too expensive.
Of course there is still other ways to go which are even faster.
I would use a collection so the type itself can keep track of its own instances:
public class Coin : MonoBehaviour
{
private static readonly HashSet<Coin> instances = new ();
public static IEnumerable<Coin> Instances => instances;
private void Awake()
{
// will automatically register itself when coming to live
instances.Add(this);
}
private void OnDestroy()
{
// will automatically unregister itself when destroyed
instances.Remove(this);
}
}
Now you can simply iterate all coins via e.g.
foreach(var coin in Coin.Instances)
{
// check if close enough for your magnet e.g.
if(Vector3.Distance(coin.transform.position, player.transform.position) <= magnetRange)
{
//TODO: e.g. add points ?
Destroy(coin.gameObject);
}
}
It is considered fairly costly and is advised against using it in the Update function.
Essentially what you wish to do is to have a list over all spawned coins, and you control the spawning. This means you could easily add the spawned object to the list when you spawn it, and - if needed - remove them when you destroy them (if you dont remove them, you need to check for null in the list).
Not perfect, but depending on what you need it might work for you (not perfect because you're looping through a list asynchronously as you're adding/removing things from it)
For simplicity's sake, let's add a static (accessible anywhere) list somewhere
public static List<GameObject> SpawnedCoins = new List<GameObject>();
var it = Instantiate<GameObject>(coinTransform, ...);
SpawnedCoins.Add(it);
StartCoroutine(RemoveCoin(it))
IEnumerator RemoveCoin(GameObject coin, float time) {
yield return new WaitForSeconds(time);
SpawnedCoins.Remove(coin);
Destroy(coin);
}
Another class
foreach (var coin in SpawnedCoins) {
// Check for null first, if you Destroy them they will be null in the list
}
Alternatively, Destroy and check for null and then every few seconds clear the list of nulls before running the loop.
This question already has answers here:
Coroutines and while loop
(3 answers)
Closed 6 years ago.
I wish to move an object over time using coroutines. I wanted to get an object from point A to point B over say 2 seconds. To achieve this I used the following code:
IEnumerator _MoveObjectBySpeed()
{
while (TheCoroutine_CanRun)
{
if(myObject.transform.position.y <= UpperBoundary.position.y)
{
myObject.transform.position = new Vector3(myObject.transform.position.x,
myObject.transform.position.y + Step, myObject.transform.position.z);
}
yield return new WaitForSeconds(_smoothness);
}
TheCoroutine_CanRun = true;
moveAlreadyStarted = false;
}
and the Step is calculated like
private void CalculateSpeed()
{
Step = _smoothness * allDistance / timeToReachTop;
}
where allDistance is the distance between the bottom and the top boundary.
_smoothness is a fix value. The thing is, the bigger I get this value, the more accurate the time gets to get from bottom to up. Note that a small value here means smoother movement. This smoothness is the time the coroutine waits in between moving the myObject.
The time is measured like this:
void FixedUpdate()
{
DEBUG_TIMER();
}
#region DEBUG TIME
public float timer = 0.0f;
bool allowed = false;
public void DEBUG_TIMER()
{
if (Input.GetButtonDown("Jump"))
{
StartTimer();
}
if (myObject.transform.position.y >= UpperBoundary.position.y)
{
StopTimer();
Debug.Log(timer.ToString());
//timer = 0.0f;
}
if (allowed)
{
timer += Time.fixedDeltaTime;
}
}
void StartTimer()
{
timer = 0;
allowed = true;
}
void StopTimer()
{
allowed = false;
}
#endregion
The results were:
When I wanted the object to reach the top under 1 second and set the _smoothness to 0.01, the time the myObject took to get to the top was 1.67 seconds. When _smoothness was 0.2s, the time to actually reach the top was 1.04s.
So why is this so inaccurate and how to make it work fine?
This smoothness is the time the coroutine waits in between moving the myObject
The mistake you're making is assuming that a co-routine waits the perfect time before executing. Rather, it probably executes on the next frame after the timeout has finished.
Assuming you want smooth motion, you want to move the object every frame (e.g. in Update or co-routine that uses 'yield return null').
Note: Each frame may take a different duration (consider 144fps vs 15fps), and you can discover this in Time.deltaTime . https://docs.unity3d.com/520/Documentation/ScriptReference/Time-deltaTime.html
My game is an over-the-shoudler endless runner style game. I have the obstacles coming down the screen using constant force. My question is, how do I make this Constant Force Script make the obstacle go gradually faster as the game progresses and the player goes longer into the game? If you try out this script yourself (just attach it to a cube or a sphere), you'll notice how it starts off slow at first then literally 2 seconds later, it goes into Rocket Speed lol. ( I am working on an Object Pooler Script also, since I do not want to waste CPU with Instantiate and Destroy) Thank you to all who attempt and help me solve this! :)
Here is the Script I made:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(ConstantForce))]
public class AddConstantForce : MonoBehaviour
{
float speed = 1.0f;
Vector3 force;
void Awake()
{
//Another way to "Require" the component.
if(!GetComponent<ConstantForce>())
gameObject.AddComponent<ConstantForce>();
//
force = GetComponent<ConstantForce>().force;
GetComponent<Rigidbody>().useGravity = false;
}
void Update()
{
GetComponent<ConstantForce>().force = force;
force += -transform.forward * speed * Time.deltaTime;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
}
}
Usually, to do stuff like this, you need 2 variables:
1. Time in which you want the speed to increase in. (For example, increase every 5 seconds) secondsToIncreaseIn.
2. Value to increase by.(For example, increment speed by 2 every time in #1 seconds) valueToIncreaseBy.
This way you have flexible ways to speed up or slow down how much your speed increments by changing either variable. With experiment setting secondsToIncreaseIn to 5 and valueToIncreaseBy to 0.0007f made the game last longer. You can increase valueToIncreaseBy to make it even last longer.You can change the startingSpeed variable if the starting speed is too slow or high.
private float speed = 0f;
private Vector3 force;
private ConstantForce cachedConstantForce;
private float counter = 0; //Dont change(Used for counting)
public float startingSpeed = 5f;//Start with 5 speed
public int secondsToIncreaseIn = 5; //Increase in every 5 seconds
public float valueToIncreaseBy = 0.0007f; //Increase by 0.0007 in secondsToIncreaseIn time/seconds
void Awake()
{
//Another way to "Require" the component.
cachedConstantForce = gameObject.GetComponent<ConstantForce>();
if (cachedConstantForce == null)
{
gameObject.AddComponent<ConstantForce>();
}
cachedConstantForce = GetComponent<ConstantForce>();
}
void Start()
{
force = cachedConstantForce.force;
GetComponent<Rigidbody>().useGravity = false;
speed = startingSpeed;
}
void Update()
{
cachedConstantForce.force = force;
force = -transform.forward * speed * Time.deltaTime;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
if (counter < secondsToIncreaseIn)
{
counter += Time.deltaTime;
}
else
{
//Time has reached to Increase value
counter = 0; //Reset Timer
//Increase Spped
speed += valueToIncreaseBy;
Debug.Log("Increased Speed!");
}
}
Initially I was going to suggest use Time.time as factor (since Time.time is constantly increasing), but then, if you have to wait for some input for the run to start, Time.time could already be to big the the time that the run actually starts.
Instead I would suggest to keep a third variable that works as Time.time (keeps updating every frame by Time.deltaTime) and use that third variable to constantly keep increasing the applied force.
For example:
float speed = 1.0f;
Vector3 force;
float time = 0;
void Awake()
{
//Another way to "Require" the component.
if(!GetComponent<ConstantForce>())
gameObject.AddComponent<ConstantForce>();
//
force = GetComponent<ConstantForce>().force;
GetComponent<Rigidbody>().useGravity = false;
}
void Update()
{
GetComponent<ConstantForce>().force = force;
time += Time.deltaTime;
force += (-transform.forward * speed * Time.deltaTime) * time;
/* Transform is negative because in my game, I have the
illusion of the objects coming down my mountain.
*/
}
NOTE: You will probably have to apply a bigger value to speed since this operation will return quite a small value. Or you could add another variable that will increase the small value returned by the operation. Also consider that once you start testing the difficulty you might need to use a bigger or smaller value than Time.deltaTime to increase time, to either make the game harder or easier.
I have a need to lerp through 10 different materials attached to one of my game objects but for some reason, the code I have written doesn't work. I have spent the past hour trying to workout why and I'm pretty burnt out.
Could someone with fresher eyes please take a look and see if I'm doing some stupid?
public class LerpMaterials : MonoBehaviour
{
public List<Material> materials = new List<Material>();
public float lerpSpeed;
int currentMaterialNo;
Material currentMaterial;
Material targetMaterial;
bool lerpingMaterial;
float lerp;
void Start ()
{
if (materials.Count < 2) return;
currentMaterialNo = 0;
currentMaterial = materials[currentMaterialNo];
targetMaterial = materials[currentMaterialNo+1];
}
void Update ()
{
if (materials.Count < 2) return;
lerp += lerpSpeed;
renderer.material.Lerp(currentMaterial, targetMaterial, lerp);
if (lerp >= 1)
SwitchMaterial();
}
void SwitchMaterial()
{
if ( currentMaterialNo >= (materials.Count - 1) )
currentMaterialNo = 0;
else
currentMaterialNo++;
currentMaterial = materials[currentMaterialNo];
targetMaterial = materials[currentMaterialNo++];
lerp = 0;
}
}
My list holds every single material and my mesh renderer also holds the required materials as well. But nothing happens other than an instance of the first material appearing in the material renderer. No other movement.
You probably need to set your lerpSpeed.
If lerpSpeed = 0, then no change will happen.
If lerpSpeed > 1, then once per frame, it will change the material.
Since the first material is the only thing showing up and not looping through every material rapidly, lerpSpeed is probably 0. However, you will end up with a problem with this function in that it will change every frame or extremely rapidly. The issue being with how lerp is being incremented.
Instead of:
lerp += lerpSpeed;
Use this instead:
lerp += lerpSpeed * Time.deltaTime;
What this will do is instead of incrementing lerp by lerpSpeed once per frame (which is dependent on the computer's performance), instead, it will increment lerpSpeed by lerpSpeed every second, which will ensure a consistent effect across every machine.