📜 ⬆️ ⬇️

Unity - Conceptual ideas and tips for newcomers igrodeva. Powerful 2D project optimization

Link to the first article in this series.

Quick entry

I thought for a long time what topic to choose this time, and decided that I would tell you about some of the chips that will help optimize your game. This is especially true for beginners, because most often the first game is a game for smartphones and tablets. And on mobile phones, no matter how many cores there are - 6 or 8, the games are still very unbalanced in terms of resource consumption. The code and its idea, which will be presented in this article, are a little more difficult to understand than the two lines of code that I cited in my previous publication. Although, I did not put it correctly - the code is easy to understand - but the threshold for entering this understanding will be slightly higher than easy (for beginners of course), you will have to sit for 5 minutes.
')
Introduction to the idea

How do you create objects in Unity from prefabs? Only Instantiate and nothing else - there is simply no other function there.

For those who do not remember or still do not know what Instantiate () is, a small reference is * instance arguments *. Here you create them (objects), and create, sometimes delete, then create again, and so on throughout the entire level. It beats on optimization and significantly - why? Because it is written this way in all Unity forums, and sometimes it can be seen in the documentation, and the documentation must be obeyed. How does Instantiate hit on pro performance?

For example, the following code: Instantiate(bullet, transform.position, tranform.rotation);

What's happening? The prefab that you put into the bullet variable is taken, free memory is searched in the depths of your device, the memory is allocated to the size of the original prefab, a bare copy of your prefab with its components is written there, then all the parameters of the components of the original prefab are copied to your new clone and it returns to the stage at a given position and at a given angle.

It must be a long time, yes. Imagine that you are doing a runner (where all objects are repeated every five in-game meters) or just a shooter is a disaster if at least every second the instruction I described above occurs. What to do? Use pooling objects.

PS: There is a video at the office. Unity website about what object pooling is and how to do it. You can see it if you speak English. But I will bring the same idea, but with a more transparent implementation of this pooling (however, it will be more capacious in terms of lines of code).

Go!

Like the previous time, I will demonstrate everything on my current project. To be honest, I didn’t initially do object pooling, but instead just created objects during gameplay. And yes, at some point, when a large number of spacecraft appeared on the screen and they all began to shoot, FPS jumps began to appear, and this is very annoying. I had to redo it.

I repeat that this implementation will be more cumbersome than from Unity (they use instances of one class, so that for a separate object (for example, a spacecraft) to create a pool of objects it needs (for example, bullets). I went the other way and created a separate so to speak script manager of object pulling.

That is, it looks like this in my Unity editor (just so that you fully understand the idea):



I like to keep everything neatly on the shelves, so I even have two separate managers (for bullets, and for different effects).

First I explain the action plan, then we look at the code:

1) In your own script that will work with pooling objects, create a List (of course);
2) At the start of the game, immediately create in the cycle the required number of objects for pooling. And in the loop at each iteration, after creating one instance of the object, disable it and add it to your List created before;
3) Go to the object script that will activate your objects in the sheet (for example, the script that controls the ship). Create a connection with the object that is your pool manager (Or make the class with pools static to apply directly). And at the right moment (for example, shooting) you in your corortina (usually korutins are used for shooting) call a function that will activate your object for the pool with the necessary parameters. Everything.

Now the same, but in practice. The pool manager code is presented (for only one RedMissile object).

 using UnityEngine; using System.Collections; using System.Collections.Generic; public class BulletsContainer : MonoBehaviour { List<GameObject> missileRed=new List<GameObject>(); //   RedMissile      public GameObject RedMissile; //  missileCount          public int misisleCount; void Awake() { //   ,          for(int i=0;i<missleCount;b++) { GameObject temp=(GameObject)Instantiate(RedMissile); temp.SetActive(false); missileRed.Add(temp); } } public void LaunchEnemyRedMissile(GameObject Caller) { if (missileRed != null) { for (int i=0; i<missileCount; i++) { //  -       //  ,       ,  //      if (!misisleRed [i].activeInHierarchy) { //    -  " missileRed [i].SetActive (true); " -   //            , //   ,       .   //     ,   ,      //      missileRed [i].SetActive (true); missileRed [i].transform.eulerAngles =Caller.transform.eulerAngles; missileRed [i].transform.position = Caller.transform.position; missileRed [i].GetComponent<DisableObject> ().StartCoroutine ("AblePause"); break; //     ,        }}}}} 

Now, regarding the additional lines.

1) " missileRed [i] .transform.eulerAngles = Caller.transform.eulerAngles; "

- As you see, I accept the Caller object in the function argument, I think everyone can guess that this is the object from which this function was called. Most likely, you will often have to do this to activate an object from the pool to the position of the caller, as is done in this next line " missileRed [i] .transform.position = Caller.transform.position; ".

2) " missileRed [i] .GetComponent () .StartCoroutine (" AblePause "); "

- so you also have to do it often. The fact is that every time you activate an object from the pool, you must deactivate it after some time (you can put a lot of deactivation conditions, for example, when a bullet collides with a player’s ship, it carries something like " this.gameobject .SetActive (false); "). Therefore, I have a script on each pool object that deactivates it after a certain time. And once again: Do not forget to deactivate the pool objects, otherwise, at some point all your objects will be active and their activation cycle will simply not be executed!

Then the code in the boat (or any of your objects that calls the pool activation function) will be:

 //....-  ..... //    ,    ,     //    : EnemiesContainer = GameObject.FindGameObjectWithTag ("ShipsGenerator"); BulletsSystem = EnemiesContainer.GetComponent<BulletsContainer> (); //....-  ..... IEnumerator StartAttack() { float randomTime = Random.Range (cooldownMin, cooldownMax ); yield return new WaitForSeconds (randomTime); //         ,    BulletsSystem.LaunchEnemyRedMissile(this.gameObject); } //....-  ..... 

Conclusion

Here is an example of the result - a large number of objects on the scene, almost 80% of the entire scene (everything except the ships) is created through the pooling of objects, you will get a slightly longer loading level, but there will be no stoppers during the gameplay.



PS: It is not necessary to use this technique if you have a leisurely game in which there are not many repetitive objects, however this technique should be used absolutely exactly if you are going to make a runner or a vigorous shooter. In general, everything, as always - everything is good in moderation.

PPS: Thank you for your attention! Have questions? Write in the comments - I will answer for sure.

Source: https://habr.com/ru/post/268663/


All Articles