What is the fastest way to get GameObject reference? - c#

Let's say my script is defined as given below.
public GameObject _GameObject;
private void Start()
{
[![enter image description here][1]][1]
_GameObject = gameObject;
}
Would this execute faster?
public void SetActive(bool value)
{
[1]: https://i.stack.imgur.com/S60FB.jpg
gameObject.SetActive(value);
}
Or this?
public void SetActive(bool value)
{
_GameObject.SetActive(value);
}

In Unity 4.x and below, the cache method would be significantly faster. In those versions, MonoBehaviour.transform and MonoBehaviour.gameObject were not actually fields; rather, "under the hood" they behaved like properties with an attached accessor.
Thus accessing the gameObject property would make a method call to Component.get_gameobject() via the accessor. Of course, a method call naturally imposes more overhead than a simple memory access. (Transform was worse; apparently the accessor actually invoked the GetComponent method to return the value!)
This is why you will often see veteran Unity developers caching these values.
I have it on good authority that this process has been streamlined in Unity 5 for better performance; using the built-in properties will still create a very small amount of overhead but it is reportedly insignificant.
Source: https://blogs.unity3d.com/2014/06/23/unity5-api-changes-automatic-script-updating/

I assumed that caching the variable is faster than using the gameObject variable from the Component class and a simple test proved that to be true. That's because caching it will give you the reference rather than using gameObject which uses the get accessor to return the reference. Not sure if getting the reference requires a native function call but that's a possibility. Using get accessor is slower than direct reference access.
Let's say you have 1 million scripts calling gameObject.activeSelf or through the cached version _GameObject.activeSelf.
Test Result:
gameObject: 54 ms
Cached _GameObject: 30 ms
Software/Hardware tested on:
Unity 5.6.0f3
Windows 10 Pro
MacBookPro11,4
16 GB RAM
Does it matter?
In a normal app, maybe not. In a game, yes. Removing 24ms from a game is a good improvement depending on the kind of Game.
Test script:
public GameObject _GameObject;
void Start()
{
Application.runInBackground = true;
int iterations = 1000000;
//TEST 1
Stopwatch stopwatch1 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
bool active = gameObject.activeSelf;
}
stopwatch1.Stop();
//TEST 2
Stopwatch stopwatch2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
bool active = _GameObject.activeSelf;
}
stopwatch2.Stop();
//SHOW RESULT
WriteLog(String.Format("gameObject: {0}", stopwatch1.ElapsedMilliseconds));
WriteLog(String.Format("Cached _GameObject: {0}", stopwatch2.ElapsedMilliseconds));
}
void WriteLog(string log)
{
UnityEngine.Debug.Log(log);
}

Related

GameObject.FindGameObjectsWithTag("Enemy").Length off by one for some reason?

so I'm wanting to pause the game once the amount of enemies hits 0. So I'm using GameObject.FindGameObjectsWithTag("Enemy").Length to find the number of enemies. I put this in a function that's called right when the enemies are instantiated so I can see the length go to 4, as there's 4 enemies spawning. When an enemy is killed the function is called again where the length is printed to console again. For some reason, on the first enemy killed the count repeats with a 4 again despite there only being 3 enemies. Once another enemy is killed it reports 3 when there's actually 2 and so on until I get to 1 when there's 0 enemies.
Here's the first snippet of code:
public class EnemyList : MonoBehaviour
{
public List<GameObject> weakMobs = new List<GameObject>();
public List<GameObject> mediumMobs = new List<GameObject>();
public List<GameObject> bossMobs = new List<GameObject>();
public List<Transform> spawningChildren = new List<Transform>();
public static int mobCount;
void Start()
{
for (int i = 0; i < spawningChildren.Count; i++)
{
GameObject newWeakMob = Instantiate(weakMobs[0], spawningChildren[Random.Range(0, 4)]) as GameObject;
}
CheckMobCount();
}
public void CheckMobCount()
{
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Length;
print(mobCount);
}
The next piece of code is where the enemy is killed and the CheckMobCount() is called again.
public void TakeDamage()
{
enemyCurrentHealth -= 25;
enemyHealthBar.SetHealth(enemyCurrentHealth);
if (enemyCurrentHealth == 0)
{
Destroy(this.gameObject);
enemyList.CheckMobCount();
//needs death animations
}
}
Here's the console messages:
Console of printed lengths
I'm self taught so I apologize if this is elementary. I've tried doing this several different ways and this is the closest I've been but I'm open to new ideas as well.
Thank you!!
As noted in this answer, the object is not actually destroyed in the current frame.
From the documentation:
The object obj is destroyed immediately after the current Update loop… Actual object destruction is always delayed until after the current Update loop, but is always done before rendering.
I also agree that using DestroyImmediate() is a bad idea.
Ultimately, your question seems to really be about pausing the game when the enemy count reaches 0, which unfortunately hasn't actually been answered yet.
In fact, you don't really need to do anything different except move the check for the enemy count to the beginning of the Update() method, and pause the game there if it's 0. Then you'll find that the component for the enemy has been destroyed at that point.
Presumably enemies are spawned before the update loop starts (i.e. before the first frame), but if not then you can use whatever logic you're already using to decide that new enemies need to be spawned, to detect the fact that you haven't spawned any yet and avoid pausing before the enemies have spawned.
Here you have attached your script to your enemy instances. And they are still alive when you are querying for the number of enemies left.
You should do the following:
public class Enemy: MonoBehaviour
{
public static int EnemyCount = 0;
private void Start()
{
EnemyCount++;
}
private void OnDestroy()
{
EnemyCount--;
}
}
And then you can query the enemy count from anywhere but just excessing the EnemyCount by Enemy.EnemyCount.
If you want to get a more difficult example then you can check out this Game Dev tutorial: https://www.youtube.com/watch?v=LPBRLg4c5F8&t=134s
Destroy is actually executed at the end of the frame. There is DestroyImmediate that is executed immidiatelly but it's not recommended to be used. What I would do is to add a field or a property to identify whether the enemy is still alive and then to check against it. Something like:
class Enemy : MonoBehaviour
{
public bool IsAlive { get; set; } = true;
}
public class EnemyList : MonoBehaviour
{
//...
public void CheckMobCount()
{
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Select(x => x.GetComponent<Enemy>()).Count(x => x.IsAlive);
print(mobCount);
}
}
And then:
public void TakeDamage()
{
enemyCurrentHealth -= 25;
enemyHealthBar.SetHealth(enemyCurrentHealth);
if (enemyCurrentHealth == 0)
{
Destroy(this.gameObject);
this.GetComponent<Enemy>().IsAlive = false;
enemyList.CheckMobCount();
//needs death animations
}
}
This can be further optimized to store the Enemy somewhere and not use GetComponent every time but you get the idea.
As already mentioned by others the issue is that Destroy is executed delayed.
Actual object destruction is always delayed until after the current Update loop, but is always done before rendering.
You could simply count only the GameObjects that are still alive, those for which the bool operator is true.
Does the object exist?
It will be false for objects destroyed in that same frame.
E.g. using Linq Count
using System.Linq;
....
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Count(e => e);
which basically equals doing
mobCount = 0;
foreach(e in GameObject.FindGameObjectsWithTag("Enemy"))
{
if(e) mobCount++;
}
There is no need for an additional property or Component.
I am suggesting you to use “DestroyImmediate” instead of “Destroy”,Then look at the result.
I have a better idea, why not just use static variables when spawning enemies?
void Start()
{
for (int i = 0; i < spawningChildren.Count; i++)
{
GameObject newWeakMob = Instantiate(weakMobs[0],
spawningChildren[Random.Range(0, 4)]) as GameObject;
mobCount++;
}
}
Do not use Linq
Do not use DestroyImmediate (it will freeze and bug your game, probably)
Avoid FindGameObjectsWithTag in loops, only in initialization.
Track your enemies in an array or list
When you destroy an enemy, remove it's reference from the list
Use the list count/length to get the real actual number.

OverlapCircleAll optimization

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.

Unity3D Prevent adding certain built-in components

I have made my own component which conflicts with some Unity built-in components (like Rigidbody conflicts with Rigidbody2D). So I need to be sure that those components will not exist together in the same GameObject. Is there a way to do it? It seems to be easy to check when my own component is added (by Reset), but what to do if Unity' built-in component is added? Is there some callback, message, or event sent when new component attached to the GameObject?
Precisions
I do not need to hide components it in the editor, or prevent adding my own components. I am asking about preventing adding certain Unity' built-in components while my component is attached. From both Editor GUI (by "add component" button) and Unity API (by GameObject.AddComponent).
There is the [DisallowMultipleComponent] attribute which prevents two of the same type from being added to the same game object. This works for subtypes as well (which is how Rigidbody and Rigidbody2d are handled).
I am not sure if this will work for you or not, as you haven't said your components are related to each other, but it is what I can find.
Is there some callback, message, or event sent when new component
attached to the GameObject?
No.
Is there a way to do it?
Yes, but a bit complicated.
If you want to prevent your custom script from being added, that would have been easy and this question should handle that.
This is complicated because you want to prevent a component written by another person(built-in) from being added to a GameObject which means that you first need a way to detect when that component has been added to a GameObject then destroy it. This has to be done every frame (Both in the Editor and during run-time).
You can call the components you don't want to be added to a GameObject blacklisted components.
Here are the steps:
1.Store the blacklisted components in an array.
private static Type[] blacklistedComponents =
{
typeof(Rigidbody),
typeof(Rigidbody2D)
//...
};
2.Get the root GameObjects in the scene and store them in a List.
private static List<GameObject> rootGameObjects = new List<GameObject>();
Scene.GetRootGameObjects(rootGameObjects);
3.Loop through each root GameObject and use GetComponentsInChildren to get all the components attached to each GameObject under that root GameObject.
private static List<Component> allComponents = new List<Component>();
currentLoopRoot.GetComponentsInChildren<Component>(true, allComponents);
4.During the loop from #3, loop through the retrieved components and check if it has any blacklisted component. If it does, destroy that blacklisted component.
for (int i = 0; i < allComponents.Count; i++)
{
//Loop through each blacklisted Component and see if it is present
for (int j = 0; j < blacklistedComponents.Length; j++)
{
if (allComponents[i].GetType() == blacklistedComponents[j])
{
Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
Debug.Log("Removing Blacklisted Component");
//Destroy Component
DestroyImmediate(allComponents[i]);
Debug.LogWarning("This component is now destroyed");
}
}
}
That's it. You or others may have few questions about this answer.
Q 1.Wonder why FindObjectsOfType and FindObjectsOfTypeAll are not used?
A 1.These functions are usually used to simplify getting everything in the scene but the problem is that they return array. Calling these functions every frame will kill your game performance since it allocates memory and will cause garbage collector to run more often.
This is why Scene.GetRootGameObjects is used which you can pass a List inside it and it will fill the list for you. It does not return array.
Q 2.Why did you pass List to GetComponentsInChildren and not return the result from it?
A 2. Technically the-same reason I explained above. I used a version of the GetComponentsInChildren function that does not allocate memory. Simply pass List to it and it will fill it up with every component it found. This prevents it from returning an array which is expensive.
I wrote a complete working code for this below but you need to improve it. That's why I explained every process so that you can either improve or rewrite it yourself. It currently prevents Rigidbody and Rigidbody2D from being added from the Editor or from code in the Editor or in a build.You can add more components you want to block to the blacklistedComponents variable. It runs in the Editor also during runtime. UNITY_EDITOR is used to remove the Editor codes and make sure that it compiles for platforms.
1.Create a script called ComponentDetector and copy every code below into it.
2.Save and go back to the Editor. That's it. You don't have to attach it to any Object. You should never be able to add Rigidbody and Rigidbody2D to any GameObject.
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class ComponentDetector : MonoBehaviour
{
//Add the blacklisted Components here
private static Type[] blacklistedComponents =
{
typeof(Rigidbody),
typeof(Rigidbody2D)
//...
};
private static List<Component> allComponents = new List<Component>();
private static List<GameObject> rootGameObjects = new List<GameObject>();
private static void GetAllRootObject()
{
Scene activeScene = SceneManager.GetActiveScene();
activeScene.GetRootGameObjects(rootGameObjects);
}
private static void GetAllComponentsAndCheckIfBlacklisted()
{
for (int i = 0; i < rootGameObjects.Count; ++i)
{
GameObject obj = rootGameObjects[i];
//Debug.Log(obj.name);
//Get all child components attached to this GameObject
obj.GetComponentsInChildren<Component>(true, allComponents);
//Remove component if present in the blacklist array
RemoveComponentIfBlacklisted(allComponents, blacklistedComponents);
}
}
private static void RemoveComponentIfBlacklisted(List<Component> targetComponents, Type[] blacklistedList)
{
//Loop through each target Component
for (int i = 0; i < targetComponents.Count; i++)
{
//Debug.Log(targetComponents[i].GetType());
//Loop through each blacklisted Component and see if it is present
for (int j = 0; j < blacklistedList.Length; j++)
{
if (targetComponents[i].GetType() == blacklistedList[j])
{
Debug.Log("Found Blacklisted Component: " + targetComponents[i].GetType().Name);
Debug.LogError("You are not allowed to add the " + targetComponents[i].GetType().Name + " component to a GameObject");
Debug.Log("Removing Blacklisted Component");
//Destroy Component
DestroyImmediate(targetComponents[i]);
Debug.LogWarning("This component is now destroyed");
}
}
}
}
public static void SearchAndRemoveblacklistedComponents()
{
//Get all root GameObjects
GetAllRootObject();
//Get all child components attached to each GameObject and remove them
GetAllComponentsAndCheckIfBlacklisted();
}
void Awake()
{
DontDestroyOnLoad(this.gameObject);
}
// Update is called once per frame
void Update()
{
//Debug.Log("Update: Run-time");
SearchAndRemoveblacklistedComponents();
}
}
#if UNITY_EDITOR
[InitializeOnLoad]
class ComponentDetectorEditor
{
static ComponentDetectorEditor()
{
createComponentDetector();
EditorApplication.update += Update;
}
static void Update()
{
//Debug.Log("Update: Editor");
ComponentDetector.SearchAndRemoveblacklistedComponents();
}
static void createComponentDetector()
{
GameObject obj = GameObject.Find("___CDetector___");
if (obj == null)
{
obj = new GameObject("___CDetector___");
}
//Hide from the Editor
obj.hideFlags = HideFlags.HideInHierarchy;
obj.hideFlags = HideFlags.HideInInspector;
ComponentDetector cd = obj.GetComponent<ComponentDetector>();
if (cd == null)
{
cd = obj.AddComponent<ComponentDetector>();
}
}
}
#endif
If you're trying to determine if the component exists before runtime, which I assume you already know, you can use the Start() method. But, like I said, I assume you already know that, so the only other way to check something like that during runtime would be to continually check for it in the Update() method, every frame. Although, I am not sure as to why a Unity component might be added to a gameobject during runtime. If this is an issue, maybe the component in question could be added to a child or parent gameobject instead?
If you really feel like making some changes, which may require a lot of refactoring for your project, you could always create a ComponentManager class that handles adding and removing components to GameObjects and create your own callbacks.

is there a point in recycling value types unity

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.

Creating an object many times and destroy it: is it good what I do?

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

Categories

Resources