So the question is this: I have states in my code. I want to divide them to regions and simply enable or disable them upon some conditions. The code is in a looping Unity function named LateUpdate. I wish to do this to save up on processor time instead of using booleans or enums.
Can I do that? if so, how?
Add. info: I need to do this disabling on runtime. The script is an image sequence player that plays gifs. I have a warm up part then a looping part. I can write it with bools but as I said I need to save on processor time as this will be a mobile game.
some code:
void LateUpdate ()
{
if (warmUp)
{
currentImageIndex = Mathf.RoundToInt(Time.time * frameRate);
currentImageIndex = currentImageIndex % spriteSheet.Length;
spriteRenderer.sprite = spriteSheet[currentImageIndex];
warmUp = false;
}
if (updateEnabled && !warmUp)
{
switch (playMode)
{
case PlayMode.order:
currentImageIndex = Mathf.RoundToInt(Time.time * frameRate);
currentImageIndex = currentImageIndex % spriteSheet.Length;
spriteRenderer.sprite = spriteSheet[currentImageIndex];
break;
case PlayMode.random:
updateEnabled = false;
StartCoroutine(RandomizedPlay());
break;
}
}
}
Simple if checks like you are currently doing already is the correct way to disable code at runtime that does not always need to run inside a function.
You should learn how to use the unity profiler on the mobile device, the source of your slowdown (if you are having any) are not from your if statements themselves.
I would say it is extremely likely that each of those if checks won't even get above 0.00% of the CPU time during the profiler.
Related
I've made boids in unity but when trying to render a 1000 of them the performance is really bad, in my update function i use Physics.OverlapCircleAll to check all surroundiing boids. Is there any way to do this more optimized? Here is my update function:
void Update()
{
Collider2D[] hitColliders = Physics2D.OverlapCircleAll(Position, radius,layerMask.value);
List<Boid> boids = hitColliders.Select(o => o.GetComponent<Boid>()).ToList();
boids.Remove(this);
Flock(boids.ToArray());
}
Absolutely! Physics.OverlapCircleAll creates a lot of garbage every time it is called. What you're looking for is Physics.OverlapCircleNonAlloc, which will not create any garbage as it uses a buffer:
Collider2D[] hitsBuffer = new Collider2D[30]; //limit the amout of possible boid interations
void Update()
{
int numHits = Physics2D.OverlapCircleNonAlloc(Position, radius, hitsBuffer, layerMask.value);
Flock(hitsBuffer,numHits);
}
void Flock(Collider2D[] hitsBuffer, int numHits){
for(int i = 0; i < numHits; i++){
var boid = hitsBuffer[i].GetComponent<Boid>();
if(boid == this)
continue;
//flocking algorith here
}
}
Note how in the above code no additional arrays are created each frame, which is quite expensive. To check how much time is being spent where check out the Profiler:
Orange is 'Physics', working out the overlaps
Cyan is 'Scripts', calcuations in code, ie the flocking algorithm
Dark green is 'GarbageCollector', handling arrays created and destroyed each frame
PS If not already, ensure that the boids are using a CircleCollider2D, this is the easiest for Unity to calculate.
PPS You may want to double check that if(boid == this) actually gets called. I thought that Physics.Overlap... ignores this collider.
Hey before I start I want to let everyone know I'm not only new to this forum but Unity and C# itself so I apologize if there is an easy solution or other silly mistakes.
Alright so basically what I am trying to do is switch the gravity of my player when they hit space, to achieve this I am checking the players transform.position.y and seeing if it is at it's designated height and if it's not I add force.
The area of code:
private void ChangeGravity()
{
if (rb.position.y >= 10f)
{
SAF = false;
rb.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionZ;
}
else
{
rb.AddForce(0, VerticalForce * Time.deltaTime, 0);
ChangeGravity();
}
}
For clarification SAF is a precautionary measure so that the player can't spam the space button.
Also VerticalForce = 2f and through my testing I have determined that it is possible for the if statement to be true (this test was by setting the y to 10)
Now here's the error:
StackOverflowException
UnityEngine.Rigidbody.AddForce (Vector3 force, ForceMode mode)
UnityEngine.Rigidbody.AddForce (Single x, Single y, Single z) (at C:/buildslave/unity/build/Runtime/Dynamics/ScriptBindings/Dynamics.bindings.cs:171)
PlayerMovement.ChangeGravity () (at Assets/Scripts/PlayerMovement.cs:21)
PlayerMovement.ChangeGravity () (at Assets/Scripts/PlayerMovement.cs:22)
(The final line repeats a bunch but I cut that out)
The Entire Script: The Script
EDIT
I finally found a very helpful tutorial that i wouldnt have found without you guys on how to reverse gravity of an object, this makes this question obsolete, thank you for your time i'm sorry i didn't find this before making the question.
Your method does not return, it calls itself which in turn calls itself again.
Since calling a function allocates some memory on the thread’s stack, the stack is soon overflowed since it has a space limit of a few MegaBytes.
Call this method in while loop, break the loop when the condition is met. So in each iteration of the loop the method is returned and the call stack won’t grow.
while (true)
{
if (rb.position.y >= 10f)
{
SAF = false;
rb.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionZ;
break; //break the loop since condition is met
}
else
{
rb.AddForce(0, VerticalForce * Time.deltaTime, 0);
continue; //the condition is not met, so the loop goes on
}
}
I use the function Application.LoadLevelAsync (s); in my game but it just stops at 89%. Help me. It's my code:
public void LoadScenes (string s)
{
if (!quit) {
SoundController.PlaySound (soundGame.ButtomClick);
progressBars.gameObject.SetActive (true);
background.SetActive (true);
text.gameObject.SetActive (true);
async = Application.LoadLevelAsync (s);
async.allowSceneActivation = false;
StartCoroutine (DisplayLoadingScreen ());
}
}
IEnumerator DisplayLoadingScreen ()
{
while (!async.isDone) {
loadProgress = (int)(async.progress * 100);
text.text = "Loading Progress " + loadProgress + "%";
progressBars.size = loadProgress;
yield return null;
}
if (async.isDone) {
Debug.Log ("Loading complete");
async.allowSceneActivation = true;
}
}
When you set async.allowSceneActivation = false, the loading stops at 90% or 0.9. This is because unity saves the last 10% to actually show you the scene that has been loaded. So 0 to 0.9 is when the actual loading process takes place and the last .1% is for displaying the loaded scene.
So in your case, you are checking for async.isDone which would become true only when progress is 100% but you have also set async.allowSceneActivation = false which would stop the progress at 90%. What you might want to check is async.progress >=0.9f and then set your async.allowSceneActivation = true.
Reference: https://docs.unity3d.com/ScriptReference/AsyncOperation-progress.html
From my experience, AsyncOperation.progress can cap out at about 89% as you've discovered. It may look like it's stalled at that message, when really it's still loading for however long it needs. It won't ever reach 100% even when it's done though, so it's rather useless if you want to implement a loading bar.
As such, async.isDone might never become true.
As a workaround, you could set async.allowSceneActivation = true; sooner than later, ignoring async.progress altogether:
async = Application.LoadLevelAsync (s);
async.allowSceneActivation = true;
I tend to use a progress-agnostic rotating loading icon instead of a loading bar because of this issue.
On a side note that I haven't tested, others have said that this is an Editor-only issue; async.progress and async.isDone might actually work on standalone builds.
Hi I am new to Unity game engine and I am creating a 3d shooting game. In the level 1 I want to shoot 5 enemies in certain amount of time let's say 30sec. After completing level 1, I want to go to level 2 where my total enemies are 10 and want to kill it in 60sec and if fail there will be game over. I wrote some script for it, it works a little bit but it is not perfect because after starting level 2 the game become slow and after game over the level 2 restart again but not with the default value of 10 enemies, rather it starts from the no. which reaches at the time of game over. needs some idea and good logic and script for my game. here is my code.
public class Status : MonoBehaviour
{
public static int TotalZombies=5;
public static float timeLeft=25.0f;
// destry this game object.
Destroy (this.gameObject);
TotalZombies--;
}
and here is my other script where I am handling my levels and time etc.
using UnityEngine;
using System.Collections;
public class Generate : MonoBehaviour {
public GUIText Zombiesobject;
public string zombiesscore;
public GUIText countdown;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
zombiesscore = "Zombies Left: " + Status.TotalZombies.ToString ();
Zombiesobject.text = (zombiesscore);
Status.timeLeft -= Time.deltaTime;
if (Status.timeLeft <= 0.0f && Status.TotalZombies > 0)
{
countdown.text = "Game Over";
Application.LoadLevel(Application.loadedLevel);
}
else if (Status.timeLeft <= 10.0f && Status.TotalZombies > 0)
{
countdown.text = "Time left = " + (int)Status.timeLeft + " seconds" + " \n You are running out of time!";
}
else if (Status.timeLeft > 0.0f && Status.TotalZombies <= 0) {
countdown.text = "You win!";
Application.LoadLevel("level 2");
Status.TotalZombies=10;
Status.timeLeft=59.0f;
}
else
{
countdown.text = "Time left = " + (int)Status.timeLeft + " seconds";
}
}
}
Your problem is that you use static variables. static's are exactly like global variables. The only difference is that you need to access those through a Class name.
So what does that exactly mean? When you load your game, and when the class itself gets loaded the static variables are created and initialised. In your case it is TotalZombies set to 5 and timeLeft to 25f.
But those variables persist and never gets re-initialized as long as your game runs. Even if you do a Application.LoadLevel these variables and their values persists.
That means if you change those variables, and reload your level TotalZombies and timeLeft still have their last values.
Because of this i encourage to never use static variables. They easily introduce hard to spot bugs. Let's for assume a simple fix to your code.
You additionally add an initialization to your Start() Method. For example in your Status class you add.
void Start() {
TotalZombies = 5;
timeLeft = 25.0f;
}
In your case it could solve the problem completely, but you also could say this is just by accident or luck.
In Unity there don't exists an order in which Start() is called. It could for example still happen that the Start method in your Generate class is called first on loading a Scene. If you used Status.TotalZombies or Status.timeleft in Start to initialize something in Generate you still have your bug that your initializiation is wrong because it uses the variables from the previous run. The problem is Unity could sometimes first execute Status.Start() before Generate.Start() sometimes the other way around. That would lead to a bug that just sometimes occur and is extremely hard to debug.
If you knew the above you could also put your initialization in the Awake method. Because Awake methods will be called before any Start method. So this will be a better fix.
But there exists other problems. For example lets look into your Generate.Update() method. You for example directly do a Status.timeLeft -= Time.deltaTime; in your Update method. But when you for example have multiple GameObjects in your game that has the Generate Component it means timeLeft will be decreased multiple times in a single frame. If you have two Generate Components it means your time will run out twice as fast.
So even putting an initialization into Start or Awake can fix some bugs, but you still have different problems with statics
That is a reason why i encourage not to use static at all. So how do you fix also this problem? Instead of having static you should create Attributes of a class. And on top of that you should make all your attributes only set-able only from your own class. That also has an impact on other code. For example you could not reduce the timeLeft attribute anymore from Generate. That sounds like a disadvantage, but it forces to think you about how to change timeLeft correctly. In your case you do not really want that any class from everywhere can change timeLeft. It is a time that should be constantly reduced and it is just an error to reduce it multiple time. The result of that is. Your Status class should only change the timeLeft in the Update. The same goes for TotalZombies. It would be better to just have a method like IncrementTotalZombies and DecrementTotalZombies instead of doing Status.TotalZombies++ and so on. For example your Status class should now look like
public class Status : MonoBehaviour {
public int TotalZombies { get; private set; }
public float TimeLeft { get; private set; }
void Awake() {
this.TotalZombies = 5;
this.TimeLeft = 25f;
}
void Update() {
this.TimeLeft -= Time.deltaTime;
}
public void IncreaseTotalZombies() {
this.TotalZombies++;
}
public void DecreaseTotalZombies() {
if ( this.TotalZombies <= 0 ) {
throw new ApplicationException("Cannot decrease TotalZombies. Already 0. Possible Bug in your code.");
}
this.TotalZombies--;
}
}
Now the IncreaseTotalZombies or DecreaseTotalZombies sounds like overhead, but you can do a lot of extra checking here. For example check if the counter never gets smaller than zero. Because when it does, you have a bug somewhere in your code. For example Increasing your TotalZombies accidentally by two, or somewhere else decreasing it by two and so on. You also could implement a MaxTotalZombies attribute that ensures that you never get more Zombies as defined. And if that happens it will throw an Exception pointing it to your code directly where it happened.
It is also easier to identify bugs. Because Increasing it twice in a row looks wrong.
status.IncreaTotalZombies();
status.IncreaTotalZombies();
where following code can just look right
Status.TotalZombies += 2;
But if you do the above changes you will see your current Status.TotalZombies will not work anymore. You also have to change how to get an instance of your Status class. For this lets assume you create a GameObject in Unity named Status. Then in your Generate class you should add the following.
private Status status;
void Awake() {
this.status = GameObject.Find("Status").GetComponent<Status>();
}
Now you can replace the Status.TotalZombies++ and so on with status.IncreaseTotalZombies(). If you just want to get the values, you still just can write status.TimeLeft but setting the value status.TimeLeft -= Time.deltaTime will now throw an error. And you don't need to set it anymore because that is a behaviour that the Status class already handle in his Update Method.
Now additonally in your Generate class you had code like this.
Application.LoadLevel("level 2");
Status.TotalZombies=10;
Status.timeLeft=59.0f;
This didn't work as expected. Because when you call Application.LoadLevel() your new Scene gets called and the lines behind it was was never called. You could fix this be changing the order.
Status.TotalZombies=10;
Status.timeLeft=59.0f;
Application.LoadLevel("level 2");
Because your Status where static the value persists through loading. But the whole approach is still not really good. The Problem is you hardcode values in your code. And it seems you want different amount of Zombies and Time for every level. If you want that you can just add Attributes to your Status class that initialize your variables, And those variables are set-able through your Unity IDE. For example add the following attributes to your Status class.
public int _StartZombies = 5;
public float _StartTime = 25f;
If you add this to your Status class now in your IDE two TextBoxes will appear named Start Zombies and Start Time. In this boxes you can now enter how many Zombies or how much Start time your Level should have. The default values are 5 and 25 for those values. But those values didn't get applied on loading your level. To also apply those values when your level gets loaded change your Awake method to.
void Awake() {
this.TotalZombies = this._StartZombies;
this.TimeLeft = this._StartTime;
}
Now this.TotalZombies and this.TimeLeft always get the values that you have configured in your IDE. The only thing you now need to do is to write.
Application.LoadLevel("SomeLevel");
And you just can configure the amount of Zombies and time through your IDE! It also means you now have reusable Components. And you configure things where it belongs!
You also described that you want different conditions to loading a new Level. For example if a user is able to kill all the Zombies in a specific amount of time he directly jumps to Level 3 instead of Level 2 and so on. So how can you add this, without creating a lot of special classes?
At first you need a class on its own that just hold data. In your case you want a specific time and a definition which level gets loaded. So you could write something like this.
[System.Serializable]
public class LoadLevelData {
public float TimeLeft;
public string LoadLevel;
}
But in my opinion that logic belongs to the Status class, so what you now do is add the following to this class.
public LoadLevelData[] _NextLevels;
As soon as you add that to your code. In the Unity IDE you will see "Next Levels" with a "Cursor". You now can expand this cursor and a Size field will appear. You now can for example write 2 into it and it gives you Element 0 and Element 1. So Unity gives you the ability to create an Array of objects and you can create as many entries as you want from the IDE with any values that you want!
Now you could write a LoadNextLevel Method in such a way.
public void LoadNextLevel() {
foreach ( var level in this._NextLevels ) {
if ( level.TimeLeft > this.TimeLeft ) {
Application.LoadLevel(level.LoadLevel);
}
}
}
Now you can configure in the Unity IDE
Element 0:
Time Left -> 20
Next Level -> "Level 3"
Element 1:
Time Left -> 10
Next Level -> "Level 2"
You only need to call status.LoadNextLevel() when your game finished. And you can configure everything from the IDE. Also note. The order in which you fill your _NextLevel Array is important. In this case "Time Left" -> 20 must be before "10".
I'm using the Texture2D.GetData() method to create a Tilemap. This always works the first time I execute it, but the second time the method simply returns other Colors, confusing the whole algorithm. I see absolutely no reason for this.
My code:
switch (KartenID)
{
case 1: WasserKarte = Content.Load<Texture2D>("Karte/Karte1");
break;
case 2: WasserKarte = Content.Load<Texture2D>("Karte/Karte2");
break;
default:WasserKarte = Content.Load<Texture2D>("Karte/StandardKarte");
break;
}
KARTE_BREITE = WasserKarte.Width;
KARTE_HOEHE = WasserKarte.Height;
Color[] c = new Color[KARTE_BREITE* KARTE_HOEHE];
WasserKarte.GetData<Color>(c);
//and so on...
I am really frustated by now. I can't really tell if the Content.Load<> returns a wrong Texture, but I don't think so. Its just the second time I'm running this code without restarting the application.
I would really appreciate help.
Same texture should always return same color data. For example:
var texture = Content.Load<Texture2D>("img");
var clr = new Color[texture.Width*texture.Height];
texture.GetData(clr);
texture = Content.Load<Texture2D>("img");
var clr2 = new Color[texture.Width*texture.Height];
texture.GetData(clr2);
for (int i = 0; i < clr.Length; i++)
{
if (clr[i] != clr2[i])
{
// never gets hit
Console.WriteLine("Error - color {0} != color {1}", clr[i], clr2[i]);
}
}
Since you didn't provide more code I can only assume, that you doing something not intentional to a texture at runtime. However there is a simple way to fix your problem and optimize the code at the same time. Create class for storing textures information, something like this:
class Resource
{
public Texture2D Texture;
public Color[] ColorData;
}
Dictionary<string, Resource> Resources;
Instead of calling Content.Load each time you need to get color information, read all textures that is used in the level, then assign texture identifiers and get color data for each texture only once. Later just reuse already loaded color data from resources:
var colorData = Resource["KartenID"].ColorData;
// do something with color data
Of course this is just very simple example of game resources management, you can write more sophisticated resources manger to meet your needs.