I'm working through a book that is helping me learn C#, and one of the projects is something like one of those old games one is taught in elementary powerpoint lessons. This particular example uses a for loop that defines how many exits (external doors) a room or area has.
This is an example of moving through an external door. When I go back through the door, using the "MoveToANewLocation()" method, "currentLocation" loses it's value. The for loop subsequently sets the value to a negative, resulting in an error.
private void MoveToANewLocation(Location newLocation)
{
currentLocation = newLocation;
exits.Items.Clear();
for (int i = 0; i < currentLocation.Exits.Length; i++)
{
exits.Items.Add(currentLocation.Exits[i].Name);
}
exits.SelectedIndex = 0;
description.Text = currentLocation.Description;
if (currentLocation is IHasExteriorDoor)
{
goThroughTheDoor.Visible = true;
}
else
{
goThroughTheDoor.Visible = false;
}
}
I have a reference example that is exactly the same as the above, which works. I am stumped as to why currentLocation is losing it's value when the button "goThroughTheDoor" invokes the "MoveToANewLocation()" method.
Apologies if this is unclear, I am still very new to modern programming
When the MoveToANewLocation method begins, it sets currentLocation to a copy of the parameter, newLocation:
//currentLocation = newLocation;
The currentLocation goes out of scope when the method exits and the garbage collector can clean up to use that memory for in scope objects. This explains how its value is lost after exiting the method.
Related
Please look at example - http://www.mathplayground.com/mancala.html
Can anyone suggest the logic to :
1) spawn objects at positions
2) Pick up all objects on click and distribute them one by one.
3) Is it better to create all objects or instantiate them on the fly. ?
I tried code below but it just instantiates all objects at once.
if (HoleHandler.gemCount_Hole1_Update_Flag == true)
{
foreach (GameObject g in gemList1)
{
Destroy(g);
//want to add a time delay of 2 secs here
}
if (gemCount_Hole1 > 0)
{
for (int i = 0; i < gemCount_Hole1; i++)
{
int Gem_prefabIndex = UnityEngine.Random.Range(0, 9);
gemList1.Add(Instantiate(Gem_prefabList[Gem_prefabIndex], new Vector2((xPos_Hole1 + (Random.Range(-20, 20))) * 2.0F, (-229 + (20 * i))), Quaternion.identity));
}
}
}
I'm not 100% sure of what you're trying to achieve but I will answer as best I can.
For a start, any gameobject you are going to be instantiating (spawning) at run time should ideally be done so from a prefab.
Secondly, to spawn them at random intervals you want to be checking if they should be spawned at different time frames. This can be achieved through a co-routine or the Update function. I would recommend Update if this is new to you.
Update is called every frame.. and it's with this that you can achieve timed events. You can use a variety of helper methods to determine the time since the last frame or the real time elapsed.
For example
public class MyGameObject : Monobehaviour {
void Start() {
//This is called first, use it to set up whatever you want.
}
void Update() {
//This will be called every frame.
//Each frame or time lapse will determine if I should spawn
// a new gameobject.
}
}
Update
After looking at the game you have linked in your post I can offer the following advice.
Something like the following may point you in the right direction.
public int[] gemsInCups = new int [] {4,4,4,4,4,4,0,4,4,4,4,4,4,0};
public void Distribute(int position){
int gems = gemsInCups[position];
for(int i = position + 1; gems > 0; i++){
gemsInCups[position] ++;
gems --;
//Check the end of the array has not been reached.
//If it has, start distributing again from the first position provided
// there are still gems to distribute.
}
}
You will need some additional logic to finish this.
What you should remember is, I usually find it much more manageable keeping my data and my view (gameobjects) under different scopes... but the view will change to reflect the data and does not directly alter it. Now you know how many gems there are in each cup, you can simply update this each frame.
I found article stating that recycling and reusing variables is good practice in unity. So I adopted it.
But one thing not clear : does this apply to value type variables (integers, vectors)?
is there a point i using this:
int x;
Vector3 v;
void functionCalledVeryOften(){
x=SomeCalculation();
v=SomeCalc();
//do something with x and v
}
instead of this:
void functionCalledVeryOften(){
int x=SomeCalculation();
Vector3 v=SomeCalc();
//do something with x and v
}
Is there a point in recycling value types unity
Yes, some datatypes not all.
does this apply to value type variables (integers, vectors)?
No.
It depends on the variable type.
This does not apply to int, double, float, bool, Vector3 and Vector2 and other similar datatypes. It does not even apply to string because already, string cannot be re-used in C#. strings are immutable.
In-fact, using int from a local variable, lets say in a while loop is faster than using int declared as global.
*Examples of when you should declare variable once and re-use it or in your own words, recycle or re-use variables in Unity*.
Arrays:
If a function contains array and that function is often called.
void functionCalledVeryOften()
{
float[] playerLives = new float[5]; //This is bad because it allocates memory each time it is called
for (int i = 0; i < playerLives.Length; i++)
{
playerLives[i] = UnityEngine.Random.Range(0f,5f);
}
}
This allocates memory each time and can be solved by making the array global and initializing it outside the function once. You can create a simple function that resets the data in the array into 0.
float[] playerLives = new float[5];
void functionCalledVeryOften()
{
for (int i = 0; i < playerLives.Length; i++)
{
playerLives[i] = UnityEngine.Random.Range(0f,5f);
}
}
Creating new Objects:
Creating new Objects takes resources and can cause problems on mobile devices. This depends on how often you do this.
The code below creates a GameObject (bullet) then attaches Rigidbody to it and then shoots it.This happens every frame while space bar is held down and finally destroys the bullet 10 seconds later.
void functionCalledVeryOften()
{
if (Input.GetKey(KeyCode.Space))
{
//Create new Bullet each time
GameObject myObject = new GameObject("bullet");
Rigidbody bullet = myObject.AddComponent<Rigidbody>() as Rigidbody;
//Shoot Bullet
bullet.velocity = transform.forward * 50;
Destroy(myObject);
}
}
The code above is bad as it allocates memory each time new GameObject is created and when the GameObject is destroyed, it will also trigger garbage collector. This can slow down and cause hiccups in your game.
The solution to the above code is Object pooling. You can learn more about it here: Object Pooling tutorial from Unity
Example of simple fix for this with a global variable:
List<GameObject> reUsableBullets;
int toUseIndex = 0;
void Start()
{
intitOnce();
}
//Call this function once to create bullets
void intitOnce()
{
reUsableBullets = new List<GameObject>();
//Create 20 bullets then store the reference to a global variable for re-usal
for (int i = 0; i < 20; i++)
{
reUsableBullets[i] = new GameObject("bullet");
reUsableBullets[i].AddComponent<Rigidbody>();
reUsableBullets[i].SetActive(false);
}
}
void functionCalledVeryOften()
{
if (Input.GetKey(KeyCode.Space))
{
//Re-use old bullet
reUsableBullets[toUseIndex].SetActive(true);
Rigidbody tempRgb = reUsableBullets[toUseIndex].GetComponent<Rigidbody>();
tempRgb.velocity = transform.forward * 50;
toUseIndex++;
//reset counter
if (toUseIndex == reUsableBullets.Count - 1)
{
toUseIndex = 0;
}
}
}
So basically, you create an Object inside a function before Game begins, then store the reference in a global variable. You will then re-use that Object you created in the function since its reference is held in a global variable.
Instantiate:
The Instantiate function is used to create of copy of a prefab.
The code below will instantiate a bullet then shoots it every frame while space bar is held down and finally destroys it 10 seconds later.
public GameObject bulletPrefab;
void functionCalledVeryOften()
{
if (Input.GetKey(KeyCode.Space))
{
//Create new Bullet each time
Rigidbody bullet = Instantiate(bulletPrefab, new Vector3(0, 0, 0), Quaternion.identity) as Rigidbody;
//Shoot Bullet
bullet.velocity = transform.forward * 50;
Destroy(myObject,10f);
}
}
The code above is bad as it allocates memory depending on how many components is attached to the bullet prefab and how much child GameObject is under it. The solution is also use Object Pooling. Instantiate the GameObject in a function, store the reference in a global variable then re-use them. The solution is the-same with the solution above.
In conclusion, the example code in your question does not apply this.
You can learn more about Memory Management in Unity here.
This is fairly dependent on what you wish to do with this object.
Lets take your first example, say we would want to access the variables x & v from a second function functionCalledEveryOnceSoOften() This function won't need any overloads to pass the variables, and can just directly access the variables in the instance of the class.
With the second example, if we wanted to do the same thing. We would have to call functionCalledEveryOnceSoOften(int, vector3) As the function would not have direct access to the variables.
In unity it is often the case that a function will need to use the same values as another function, all though they might not always be called in chain's. To accommodate this in your 2nd example, we would have to add if statements inside our function to filter this out.
In your first example however, we could use these variables without a issue. This is one of the reasons it is often advised to do so.
As per the performance, in your 2nd example the variable is stored in the stack as opposed to the heap, because it is defined within the confines of a method which will get destroyed once the method ends executing. So the variable's memory usage is not really a concern. There might be a small overhead for the repeated creation and destruction, but this should be insignificant.
In your first example you will store the variable on the heap, as it is defined within the scope of the class, it will only be destroyed along with the class, and created on it's instantiation. This means that memory might be used over longer periods of time, but there will be no overhead for creating/destroying the variable. This also is usually insignificant.
All together, unless you are instantiating thousands of these objects, accessing the variables in rapid succession you will most likely not notice a lot of difference in performance.
The biggest difference will most likely be the way code is written. For better, or for worse.
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 have a function that loads gameobjects (in particular it generates its cache on a texture, in order to use it later)
// This function is called during the game loading screen (with progress bar)
public void Loader()
{
for (int i = 0; i < game_objects_.Count; ++i) // ~250
{
game_objects_[i] = new GameObject();
game_objects_[i].DrawCache(spriteBatch);
}
GC.Collect();
}
// Inside GameObject class
...
public GameObject()
{
// ...
// GameObject is composed of 3 (different) objects, to be drawn in one texture.
piece1_ = new Piece1();
piece2_ = new Piece2();
piece3_ = new Piece3();
}
public void DrawCache(spriteBatch s)
{
// On RenderTarget2D cache_
piece1_.Draw(s);
piece2_.Draw(s);
piece3_.Draw(s);
// Due to the fact that I won't use anymore these 3 objects, but just the Whole "cache_"
// I can "destroy" them
piece1_.Dispose();
piece2_.Dispose();
piece3_.Dispose();
}
Am I doing it right? I'm asking this because AFTER the loading screen (at the end of Loader function), I still get some "little random freeze" in the game for about 2 seconds, so I thought that maybe the GC is doing something although the function has finished, or maybe I have a wrong undertanding of how Dispose() is being used.
Avoid GC.Collect(); unless you are really sure of what you are doing : C# is not C++, you do not control the lifetime of an object, and in particular you do not control the moment when memory will be freed. Calling GC.Collect() usually creates more issues, and fixes none.
Unless your program consumed almost all available memory (you should check how much memory, there is no reason why the GC would make your application freeze.
Notes:
Inside GameObject, you should put pieces in a List and iterate over it : code will be easier to maintain and to read.
Consider using statment for automating (and enforcing) calls to Dispose()
Example:
public interface IPiece : IDisposable
{
void Draw(spriteBatch s);
}
// ...
public void DrawCache(spriteBatch s)
{
// On RenderTarget2D cache_
foreach(var piece in this.pieces)
{
using(piece)
{
piece_.Draw(s);
}
}
}
I am trying to programm an edge detection method. And I have used emgucv Image class. Since I need gray values I have declared it as
Image<Gray,float> MyImage = new Image<Gray,float>;
I select an image and assign its pixel values into MyImage as
public void selectImage()
{
OpenFileDialog opp = new OpenFileDialog();
if (opp.ShowDialog() == DialogResult.OK)
{
MyImage = new Image<Gray,float>(opp.FileName);
InputArray = new Image<Gray, float>(opp.FileName);
Convert.ToString(MyImage);
pictureBox1.Image = MyImage.ToBitmap();
}
}
When I click on edge detection button it calls the main recursive function
private void detect_edges_Click(object sender, EventArgs e)
{
hueckel_operator(1, 1);
}
This operator repeats itself with 5pixel intervals. In other words I apply it on x axis by incrementing x parameter by 5 and at the end of the row I increment y axis by 5 and so on.
In hueckel_operator, the function "a()" which calculates again a very heavy formula is being called 8 times. Here is the a() function
public double a(int j, int counter6, int counter7)
{
for (int II = 0; II <= j ; II++)
{
for (KK = 1; KK < 70; KK++)
{
x_value = input_i_x(KK); //this function brings the x coordinate
y_value = input_i_y(KK); // this function brings the y coordinate
result += HueckelDisk(x_value,y_value,j) * MyImage[x_value+counter6, y_value+counter7].Intensity;
//MyImage.Dispose();
}
}
return result;
}
But the problem is approximately at the coordinate (75,5) it throws a stack overflow exception. I debugged it with performance analyse and MyImage seems to eat all memory. You probably wants to see recursive function but since it is too big I can not put it here and I am sure that recursive function (hueckel_operator()) can not reach terminating condition since I found out how many times it has been called. What I want is to find out if there is another way to calculate "result" in a more efficient way.
My other question is that, object MyImage is used in function a() 69*j times and does it mean that it allocates memory space 69*j times whenever a() is called?
During my desperate tries, I have declared and defined almost every variable as global in order to reduce the memory usage because I have thought otherwise whenever hueckel_operator() and a() are called, local variables would allocate extra memory in stack over and over, is it a good or necessary approach?
I use 4 very nested and heavy functions and I don't use any class. Would it be the main problem? To be honest I don't see anything here to convert into class.
I know, I have asked too many questions but I am really desperate right now. I am reading articles since some weeks and I guess I need a kick start. Any help would be appreciated.
A stack overflow exception doesn't really have much to do with memory usage - it's from stack usage. In your case, a recursive call.
Recursion can only go so deep until the stack is exhausted. Once that happens, you get your stack overflow.
If you are not in an unbounded recursion but you need to go deeper, you can specify the stack size when you create a thread, and run your recursive function on that:
var stackSize = 10000000;
var thread = new Thread(new ThreadStart(StartDetection), stackSize);
However, the default size is 1MB - which is quite a bit for most tasks. You may want to verify your recursion is in fact not unbound, or that you can't reduce or remove it.