Why is my game lagging huge when I call a method? - c#

I've been messing around trying to create an analog style scoring system for my game that I am building with Unity3D and I've noticed a huge lag when I call AnalogScoreProcessing() from the updateScore() function:
/******************************** Update Score ********************************/
public void updateScore (int newScore)
{
// Send the old score/new score to AnalogScoreProcessing()
if (oldScore != newScore)
{
AnalogScoreProcessing(oldScore, newScore);
}
// Display GUI score
oldScore = newScore;
guiText.text = "" + oldScore;
}
/*************************** Analog Scoring System ****************************/
void AnalogScoreProcessing(int oldScore, int newScore){
for(int i = oldScore; i <= newScore; i++){
print (i);
}
}
Do I have to create a new thread or a co-routine to complete the looping task while the remaining parts of updateScore() are carried out? I've never done any threading but I have used co-routines. I'm just not sure what I should be using here.

The easiest solution is to use coroutines here.
You basically want the players to see the effect of counting up right?
So say you want to display a count up to the user being +1 to the user ever few frames or so so the count up will be perceivable.
Try...
I would do it like this
int current =0;
int target = 0;
float percentateOfTarget = 0;
float step = 0.1f;
IEnumerable AnalogScoreProcessing()
{
while(current <= target)
{
current = (int)Math.Ceiling(Mathf.Lerp(current, target, percentateOfTarget));
return yield new WaitForSeconds(step);
percentageOfTarget += step;
// Display GUI score
guiText.text = "" + current;
}
}
public void updateScore (int newScore)
{
// Send the old score/new score to AnalogScoreProcessing()
if (oldScore != newScore)
{
StopCoroutine("AnalogScoreProcessing");
current = oldScore;
target = newScore;
StartCoroutine("AnalogScoreProcessing");
}
}
Please be aware this code is pulled directly out of my head so youll probably have to tweek some things here and there but should give you something close to what you desire.
You could/should even scale the GUIText up while the coroutine running for an even more dramatic effect. Let me know how it goes.
As I side note coroutines are not separate threads in unity they are ran on unity's main thread.

Well i dont understandt the Logic behind the function, but cant you just print the score once it changes?
if (oldScore != newScore)
{
//AnalogScoreProcessing(oldScore, newScore);
Debug.log(newScore);
}
Also you have to set the GUI calls inside the
OnGUI()
function inside your file.

If there is a large difference between oldScore and newScore then the print function in AnalogScoreProcessing will be run many times.
You might want to look at using a tween to change the displayed value, as you cannot display more than one value per frame anyway. Or if you're printing to the console then... why are you doing that?
edit: have moved quick and dirty solution a more appropriate question/answer

Related

How do I execute a line of code repeatedly?

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 want to activate a same animation for few different objects trough FOR loop. Animation don't sync and go in random intervals. Here is the code:

So, firstly, my scene is made out of 9 empty objects each one having spikes that have animation to pop out of the floor. I am making a game where you should avoid spikes and other projectiles. I tested this first with sprite renderer and changing colors, it worked perfectly. But when I want to activate animations using trigger (animations start from empty game state and go to their animations, then after it finishes they return to normal). I've looked trough every thing I could think of and I could not solve this. It always starts animations for 4-6 different animations and start them at the same time, but for some reason: AFTER FIRST SHOWING, every single time after first time spikes appear, 2/3 out of 4/6 always show late but start at same time. Weirdly first time it always works greatly. Any thoughts on what may cause this?
void Update()
{
if (spikeTimer > 0)
spikeTimer -= Time.deltaTime;
if (spikeTimer<0)
{
Function();
spikeTimer = spikeCooldown;
}
}
public void Function()
{
int x = Random.Range(4, 6);
List<int> included = new List<int>();
while (included.Count < x)
{
int y = Random.Range(1, 10);
if (!included.Contains(y))
{
included.Add(y);
}
}
foreach (int i in included)
{
print("aktiviran je broj" + i);
Spikes[i - 1].GetComponent<Spike>().ActivateSpike();
}
}
Now this is the ActivateSpike method that all Spikes in the scene contain
public void ActivateSpike()
{
SpriteRenderer sr = gameObject.GetComponent<SpriteRenderer>();
if (sr.color != Color.black)
{
sr.color = Color.black;
}
else
sr.color = Color.red;
/* Animator animator = gameObject.GetComponent<Animator>();
animator.SetTrigger("Activate");*/
}
You can see that I tried changing the sprite to square and changing its color, and it works perfectly...

Unity Listing below and moving if disabled

so here is the idea i have a couple of game objects in a UI listed below each other so the idea is as follows
GameObject1
GameObject2
GameObject3
GameObject4
GameObject5
so i want to disable GameObject4 but when doing so GameObject5 will stay in the position its at i want it to automatically move up in to GameObject4's position
like so
GameObject1
GameObject2
GameObject3
GameObject5
and not
GameObject1
GameObject2
GameObject3
GameObject5
anyone have an Idea on how to do this?
Try creating an array of those objects (I think you mean UI elements) in an array, store their initial positions onStart in another array then loop to pop them from a stack.
Try fiddling with this to suit your needs:
public GameObject[] buttons;
float[] buttonPos;
private void Start()
{
buttonPos = new float[buttons.Length];
for (int i = 0; i < buttons.Length; i++)
{
buttonPos[i] = buttons[i].transform.position.y;
print(buttonPos[i]);
}
}
private void Update()
{
if (Input.GetKeyDown("space"))
{
DestroyButton(1);
}
}
void DestroyButton(int i)
{
Destroy(buttons[i]);
Stack(i);
}
void Stack(int i)
{
for ( int j= i; j < buttons.Length; j++)
{
if(j != buttons.Length-1)
buttons[j + 1].transform.position = new Vector3(buttons[j + 1].transform.position.x, buttonPos[j], buttons[j + 1].transform.position.z);
}
}
Your question is pretty unclear and incomplete; even the (missing) line-breaks are confusing...
Since it's a UI and your question contains spaces and not-spaces, I guess your gameObjects are in a VerticalLayoutGroup? Far fetched, if they are, shame on you for not stating that in your question.
If you want the elements to be disabled without the lower elements to "move up"
If I understood your question correctly, the solution would be to put your content as a child of an other one like so:
EDIT:
as #comphonia correctly suggests, it is preferable to have this menu made programmatically, with every button being a child of it's never disabled parent game object, and putting them into a List or array, such that you can disable them or reenable them individually

Make a UI Text pop for a few seconds on Unity

I'm pretty new to coding, been learning for the past year and I'm currently working on an assignment for school and i can't figure out this bit of code for the love of my life.
I have an item that when the player interacts with it, executes this:
void Update ()
{
if (isPlayerNear && Input.GetKeyDown(KeyCode.E) && Avatar.strenghtAttribute >= 2f)
{
levelUp.LevelUp();
Destroy(gameObject);
}
My level up function is basically this:
public void LevelUp()
{
playerLevelText.text = ("You have gained a level!");
strenghtAttribute++;
intellectAttribute++;
playerLevel++;
}
I'm trying to figure out how to make playerLevelText.Text appear on the screen but only appear for a few seconds and I can't figure out how to make that work. Would anyone be kind enough to give me a hand?
You could either set the text to be blank, or enable/disable the text object. I'd recommend using a coroutine for this.
void Update ()
{
if (isPlayerNear && Input.GetKeyDown(KeyCode.E) && Avatar.strenghtAttribute >= 2f)
{
levelUp.InitializeLevelUp());
Destroy(gameObject);
}
Since you are destroying the gameobject calling the coroutine, the coroutine will stop. A workaround is to call a normal function in your other script, which then calls the coroutine, so execution stays within the one script (there might be a cleaner way to do this).
public void InitializeLevelUp()
{
StartCoroutine(LevelUp());
}
public IEnumerator LevelUp()
{
playerLevelText.text = ("You have gained a level!");
strenghtAttribute++;
intellectAttribute++;
playerLevel++;
yield return new WaitForSeconds(2f);
playerLevelText.text = "";
//alternatively, set the text object inactive
}

Calculating the frame-rate in a Unity scene

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());
}

Categories