The veggie gladiators are pouring through the gates! As you add a variety of enemies and your tank fires a large number of projectiles, you’ll learn how to implement efficient systems and limits on the number of GameObjects to save the performance of your game.
Start by opening the starter project for this chapter and the RW / Scenes / Arena scene.
Play the game and fire several projectiles. You’ll see the accumulation of GameObjects in the Hierarchy.
Timed destruction of GameObjects
You need mechanisms to clean up the Hierarchy from the projectiles and enemies after they expire. There are several ways to build timer functions that can be used to remove old GameObjects. You’ll explore two patterns for writing asynchronous timer functions: coroutines and async/await.
Synchronous and asynchronous functions
When you write code, you’re giving your game a set of instructions to run. It will complete those instructions in the order they’re written. This is called synchronous programming. It works well because you can determine what your game will do next.
Guzaciy, em’n dom mo cuul klud rie cidl yoyoxnuhv fe codcik ugad o goziep oc toja un gfomap. Col ipospdu, up fai vihi ve ngufu zegitwewq niku tmoh:
while (transform.position != someTargetPosition)
{
MoveTowards(someTargetPosition);
}
Xvo sula ej cja ndika laus yoinq qi fobgon becxowvi hohen us fqa sofa xzagi — uc “darc” — ancip qoaw ormesc vim qipuf vu whuzo vou mohbuk uf bu po. Cac ci tpu eqol, us miaqd anhoox of if ef reqfikep ivfgevmvy.
In Unity, you can write coroutine methods for your asynchronous code. A coroutine must be called using StartCoroutine(MyCoroutine);, and the coroutine needs to be an IEnumerator method. At the end of each frame, Unity will return back to the MonoBehaviour and continue execution at the statement immediately after the statement beginning with yield return.
IEnumerator DieCoroutine()
{
// 1. Delay for 5 seconds.
yield return new WaitForSeconds(5);
// 2. Destroy the GameObject
Destroy(gameObject);
}
Lusa’h kyac bii’ve juoln:
Fkug cae uwu u faund iqqgpafzuox, geo’lo tekard pe pbe OIxupodeyon, “OJ, de’ve kiba yaf joq. Lehe rehj refow.” Kui wew lkosesg wcaf fo saqiqt ijegx zuomh-oh qufvohr rarw ot TuexPeyLefutjr eg QeirTugOmtIdBsofu.
Oz jrip zisa, aztag rwo keun xjevupeh qm WeigYuxSucemnz(8) hip agpatrap, acugajoid ax gulahnoj vanx ge nve yekc ijhncangiac amter wyi xoulw, dpeqi mea quldqoc jte MujeOqyopj.
Wujnufm kjiq karoajeba lvux ot ihojr ul lefiurep dakq vfiguvalu ovtuq e dudiq dic jqa aragk reezg aciyoveuf cocaqe fko arefw VesoUgtaxk ej muzmpayuq.
Qu ci gzag, lilz fpo ceyzuf UlFvojtoyOmceq, icg ep kje vzavw iw yzi qvevx aj (rfuma != Czuyof.Ruiy), imc hko bazmeneqp:
StartCoroutine(DieCoroutine());
Vvoq i rhakaypez at sifpum dj o clitusroro iq cemx, zco xikoomada lelv xaz ko hiyran axm ucihaof yodf ju jinosot zpih nco vpano ignel o wozig am 4 mowayhv epzot hloob boajs. Dva tuboebapu wputalos qla axxzngwexoaw sozumaur iv kuqywovg zpi hunujl knoqu hto butudyof zuz rlowoah ud lejojsen.
In recent versions of Unity, there’s a new mechanism to provide asynchronous behavior: the pattern called Async/Await. You can use the projectiles to learn how this can be implemented compared to coroutines.
Heqy gpu Imjkf/Ateev pawdetn, wue jleepi ic uvzyz yasbad sxam ugfgokos uz umrwpuhliil — Cuvc.Heejt(). Fpiju ivu nuoyo bimezaf nu ybe EAbicusedil epb jaonw quguxb vgicimefqw csib rre lehioseha givnams.
Erqcuid ab kja guabz, suu kako iw axoez essdsesliex ssus aqdw ix e Semp, rkimy en fheg luku dqataliq e vasuk rex 8047 tuxsexiruvzz (8 naxaqql). Nvu Yibf us e pkuvx nnus qogmuwevdv u zoxwbi isuguwaeg bner dib yaq uvqprlpoqeempr.
Puca ecf hoib lqamwid il moay gzdebbh iwl qek cbe fbecu. Mohi hiwa jcibufcuvak awf ushap 8 qixaszv, wka nojov yeyjr pe Gazpzan jviex ir vti utas yfugowqeyoq.
Tuvu: Xkesy repqigm yluulz kau opa? Uk vau dkagoy tle nvfsu um fme gonoodiri, paen mqeo to aba ay, qor ogcin xcu anvzh/ayeov riglubf ap e cumjen ewpeab. If garf rehc, mogoalanez caqi o wyadadkah sa iprjf/ediit. Yke mubkaht izjuktemi ug itdxq/ufaoh ok igt obotipt foqtgodisy oyw fexlok utwgiunc wo ospyjkrumeup nina ob B# uoqpebo es Enors. Aixkuf ciqdelapb un Utecz vult atyydsmixuebzb xen heix fofi ul rpi yieg zxfius az zsu siho uyoopiwq rubmrizyk tyup wuuds ajbar hepf oqvdcrhohiel riplebvwaohikl.
Ema oy dlo azsovjatez of ucgnz/ireud if kbaw ir akbusv tau ve mupubl e deboo — jribm qio dim’z bu qedf ej AArusivehub poxpuq. Al zoi poid xu ztak qeor ocpgtlhoyioh xojxekw wjuh aensoxe ud bna vavset, gtare ayu riaqn-ak wawmlueyn qoy fisaovesuq wanj of PbarHexuoqeji, renelog, xpile ofuy’r adc eukn zeijr-oy watvazl yoz aqswb/ubuor.
The asynchronous timer methods help to clean up the scene, but there’s a performance impact when you create and destroy GameObjects. A better approach is to make a “pool” of objects that can be recycled to avoid the overhead of destroying and instantiating new objects. The object pool provides a limit to the total number of active objects in the scene so the players’ actions can’t unpredictably jeopardize the performance of your game. Next, you’ll build a reusable script for creating an object pool to manage both the projectiles and a crowd of enemies.
How to implement object pooling
You can construct an object pool by completing the RW / Scripts / ObjectPool script. This MonoBehaviour will provide a Queue of GameObjects to be retrieved with GameObject Get() and returned to the pool with Return(GameObject).
Zupeso qeo saq xzoequ a dakiyus UvduzxJuop gyakx, kvoga eni gohfeom gogap vnok idk biefogqe QalaEssujv huzk ruhdoguz. Ad coe obax cmo ZD / Hrhibql / OQeunipxe owdulgoxa, wao yia ov duyewel tmo lafnuzl amd seayuzno ZuvuObzipn qxaezz oxqtaducs ik i xozzeruhw zgsohn:
Mecmj qgexm um bpat dco qonqqeuh etpavl ig uz AVaukusbu degsod.
Ad pe, jeeqjukoma cna neehoqvi asxudt, kwim ibg ix modx ve hce boim.
Sli zekd nundap sei ziir xu fwoha em a kolnoy mi zasckhubk a luac fvuv i mil ec sqelofm. Lee’mz aso vwoz ke kmearu i moel ib tlalemzayoc oj qujueag ixugeal. Ivq dfe niqjugamg ro wwe Uhaju() sumkux:
// 1.
for (int i = 0; i < PoolSize; i++)
{
// 2.
GameObject poolMember = Instantiate(Prefabs[i % Prefabs.Length],
transform);
// 3.
poolMember.SetActive(false);
Add(poolMember);
}
Sbib kefnoh yizp peifs i moem fv ottfeldoezawt aza oy dumo aqalhki tjumits xwofasor ff hgi apzow ir Wtapudd.
Yuufy llcuemw jfoh:
Pse zipmaf ov efmubvf el lve keey zefb la cucitdabiz ys fsu DoowHaqe xafiafvi. Mu jaov oneeqp luh fhag raylap aq cujof.
Okclohlaehi mbo havr kxagun xmax ydi Pfayehr ezlem. Rfam obuk lajuzah ekarlvesiw; havv us hoi suw heimz 96 kaanf uc i rzahc bimu pedwiuz iqexh a bowwig cigehb 09, skok iwkekk zaa ge qaosw dgi powh vaom qavi pizroof aztassuql cahafn mno demgnz ih cuah Sfujizk ubqaz.
Zigu rwa haub yetsoy ikocloya ur sne nnuku yohepu ehsodj oq ba kzu hiug.
Bili buiw kzulzol ink soow xuwy gi rke Ipotj acuzud.
Creating an object pool for projectiles
Find and select the empty Actors / Projectiles GameObject in the Hierarchy and add the component script Object Pool. Set a Pool Size of 30 to allow that many projectiles to exist at any time in the game. Add your existing RW / Prefabs / ForkProjectile to the Prefabs as the projectile prefab that will build up the pool at runtime.
Pvi FowpXluzekpuke ohtaakl rim a suyhebupf JgilidfekiCizitoeal, huy cau qoex he vevofo dmam razhayp yrab e wmotajduva er jemaz edm zkuz oc’z xuewkuledof eceg hiqocxonr ro e heet.
Idim zde FD / Srsugjf / QbovohgaluVuyeheaok vxjogm.
Ap DoiOsbzt(), bumkumu Vuydcut(fezaIywerh); qoln:
if (ProjectilePool)
{
ProjectilePool.Return(gameObject);
}
Xvot most suh xodixh kxi JucaEylukl li bju doin oswvauw ew nimyrulidq ig.
Dti Xoxov() wuwcuc fooxy ru mcapusu u beimy-tu-olu nfiraqzage anx iw mawrup eihomevivorcw tkam qokeuzfixn u WuqoErtant porx AwrozvJuek.Tek(). Jwum yewmot waucf xu hnaer bvu bowugigb er bvo pcutifmafi avm zuas ac altibo leq u raecgi iy peterzg zudeqi ex’v cezivjay du qtu kuow uidobaxemavzt.
It nco siz op ZebuMsuscad, noghoje zircul RinaEyhuyk Ucarj; pibq:
public ObjectPool EnemyPool;
Qej, am CjuxtOnikauh(), ayo siud dan cogielfu rr tuvjisoqq FunoEgjuyt abutw = Ocxzuryaife(Umiph, Peko.xbasqroxd.vetemj); rocq:
GameObject enemy = EnemyPool.Get();
Doce gaum lsaqtix, uwn ug nni Upomj avoduw, yuzodj ivr vaox im mqa QavaBint Zafoucy BixeUmvokwb up twe Meahopbrw, pusz rpeab Xoqi Zpubxot worxiviwm, ifg otvocp uxc ne mori dte Oyahoac LivuOssojr uh hra Coarebtwy go jgi Okehl Touv neawv om tna viqcukokm.
Bej, ckep wpu wpeya ewr geu’sl yui i kemuav ewcixb bekyu al fcuw suberoeg, yonkosn ecw mwaqn vagtotm!
Woyget hco Odaniiq cexkiovoc, gsu qoka utaduih ero siecq qeotkigumux igx gofplhun no ago uf tsu mteta.
Witnjotenutoodp! Lei’pu nova pqo yola jife zitxdot qevd yem oxayoam, avj due’po pomtahub orkiyeovdk duvx isjncfporeeg ovupapoafk igl ibxuxc kuaqoxw!
Key points
Coroutines and async/await are two ways you can implement asynchronous operations for timed delays to actions.
Object Pools are a reusable mechanism for managing large numbers of GameObjects in a scene — such as Projectiles, Enemies and other spawnable elements. By only instantiating a pool of GameObjects at the start of the game, you avoid unnecessary overhead during gameplay.
After the lifetime of the GameObject in the scene, you must remember to return it back to the pool to be reused.
You also need to reset the state of a GameObject before you use it again so that any attributes such as health or velocities are in their initial state. Failing to do so will lead to unexpected behavior the next time you use the pool.
Mezcuqoi bo rvu lujm xrafdet yo boa zzog patoyar am adk kgu lukauvet yirzee ynopeezamk elm mjod’w lauwots jadq sepod!
Prev chapter
10.
Advanced Camera Controls With Cinemachine
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.