Artikulu honetan, Unity-n 2D plataformarako karaktere kontrolatzaile bat garatzen jarraitzen dugu, kontrolak konfiguratzeko eta optimizatzeko urrats bakoitza ondo aztertuz.
Aurreko artikuluan, " Nola sortu 2D karaktere kontrolatzailea Unity-n: 1. zatia ", pertsonaiaren oinarria nola sortu xehetasunez eztabaidatu genuen, bere portaera fisikoa eta oinarrizko mugimendua barne. Orain, alderdi aurreratuagoetara pasatzeko garaia da, esate baterako, sarrera-kudeaketa eta kameraren jarraipen dinamikoa.
Artikulu honetan, Unity-ren sarrera-sistema berria konfiguratzen sakonduko dugu, pertsonaia kontrolatzeko ekintza aktiboak sortuz, jauziak ahalbidetuz eta jokalarien aginduei erantzun egokiak ziurtatuz.
Artikulu honetan deskribatutako aldaketa guztiak zuk zeuk inplementatu nahi badituzu, artikulu honen oinarriak dituen " Pertsonaia gorputza " biltegiaren adarra deskarga dezakezu. Bestela, " Caracter Controller " adarra deskarga dezakezu azken emaitzarekin.
Gure pertsonaia kontrolatzeko kodea idazten hasi aurretik, sarrera-sistema konfiguratu behar dugu proiektuan. Gure plataformarako, duela urte batzuk aurkeztutako Unity-ren Sarrera Sistema berria aukeratu dugu, sistema tradizionalarekiko dituen abantailengatik garrantzitsua izaten jarraitzen duena.
Sarrera-sistemak sarrera-kudeaketarako ikuspegi modular eta malguagoa eskaintzen du, garatzaileek hainbat gailuren kontrolak erraz konfigura ditzakete eta sarrerako eszenatoki konplexuagoak onartzen dituzte inplementazio-gastu gehigarririk gabe.
Lehenik eta behin, instalatu Input System paketea. Ireki paketeen kudeatzailea menu nagusitik Leihoa → Paketeen kudeatzailea hautatuta. Unity Registry atalean, bilatu "Input System" paketea eta egin klik "Instalatu".
Ondoren, joan proiektuaren ezarpenetara Editatu → Proiektuaren ezarpenak menuaren bidez. Hautatu Erreproduzitzailea fitxa, bilatu Sarrera-kudeaketa aktiboa atala eta ezarri "Sarrera-sistemaren paketea (Berria).
Urrats hauek amaitu ondoren, Unity-k berrabiarazteko eskatuko dizu. Berrabiarazi ondoren, dena prest egongo da gure kapitainaren kontrolak konfiguratzeko.
Ezarpenak karpetan, sortu Sarrerako Ekintzak menu nagusiaren bidez: Aktiboak → Sortu → Sarrerako Ekintzak . Izendatu fitxategiari "Kontrolak".
Unity's Input System sarrera kudeatzeko tresna indartsu eta malgu bat da, garatzaileek pertsonaien eta joko-elementuen kontrolak konfiguratzeko aukera ematen diena. Hainbat sarrera-gailu onartzen ditu. Sortzen dituzun sarrera-ekintzek sarrera-kudeaketa zentralizatua eskaintzen dute, konfigurazioa erraztuz eta interfazea intuitiboagoa bihurtuz.
Egin klik bikoitza Kontrolak fitxategian editatzeko irekitzeko, eta gehitu "Pertsonaia" izeneko karaktereak kontrolatzeko ekintza-mapa .
Unity-ko Ekintza-Mapa bat jokoko zeregin zehatzak egiteko hainbat kontrolagailu eta teklarekin lotu daitezkeen ekintzen bilduma da. Kontrolak antolatzeko modu eraginkorra da, garatzaileei sarrerak esleitu eta doitzeko aukera ematen die kodea berridatzi gabe. Xehetasun gehiago lortzeko, ikus Sarrera-sistemaren dokumentazio ofiziala.
Lehenengo ekintzak "Mugitu" izango da. Ekintza honek pertsonaiaren mugimenduaren norabidea definituko du. Ezarri Ekintza Mota "Balioa" eta Kontrol Mota "Vector2"-n lau norabidetan mugimendua gaitzeko.
Esleitu ekintza honi loturak Gehitu gora/behera/eskuinekoa/ezkerreko konposatua hautatuz eta WASD tekla ezagunak dagozkien norabideetan esleitu.
Ez ahaztu zure ezarpenak gordetzea Gorde aktiboa sakatuta. Konfigurazio honek bermatzen du "Mugitu" ekintzarako loturak berriro esleitu ditzakezula, adibidez, gezi-teklak edo are gehiago gamepad joystick batera.
Ondoren, gehitu ekintza berri bat - "Jauzi". Mantendu Ekintza Mota "Botoi" gisa, baina gehitu Interakzio berri bat - "Sakatu", eta ezarri Trigger Jokabidea "Sakatu eta Askatu", botoia sakatu eta askatu hartu behar baitugu.
Honek karaktereen kontrolaren eskema osatzen du. Hurrengo urratsa ekintza hauek kudeatzeko osagai bat idaztea da.
Pertsonaien kontrolerako sortu ditugun Sarrera Ekintzak CharacterBody
osagaiarekin lotzeko garaia da, pertsonaia gure kontrol komandoen arabera eszenan zehar aktiboki mugitzeko aukera emanez.
Horretarako, mugimenduaren kontrolaz arduratzen den script bat sortuko dugu eta CharacterController
izendatuko dugu argitasunerako. Script honetan, lehenik eta behin oinarrizko eremu batzuk definituko ditugu. CharacterBody
osagaiari erreferentzia bat gehituko diogu, _characterBody
, scriptak zuzenean kontrolatuko duena.
Pertsonaiaren mugimendu-abiadura ( _speed
) eta jauziaren altuera ( _jumpHeight
) parametroak ere ezarriko ditugu. Gainera, _stopJumpFactor
eremuaren helburua zehaztuko dugu.
Baliteke 2Dko plataforma askotan jauzien altuera kontrolatu daitekeela konturatu izana. Zenbat eta luzeagoa izan jauzi botoia, orduan eta gorago egingo du jauzi pertsonaiak. Funtsean, hasierako goranzko abiadura ezartzen da jauziaren hasieran, eta abiadura hori murriztu egiten da botoia askatzean. _stopJumpFactor
ek jauzi botoia askatzean goranzko abiadura zenbateraino murrizten den zehazten du.
Hona hemen idatziko dugun kodearen adibide bat:
// CharacterController.cs public class CharacterController : MonoBehaviour { [SerializeField] private CharacterBody _characterBody; [Min(0)] [SerializeField] private float _speed = 5; [Min(0)] [SerializeField] private float _jumpHeight = 2.5f; [Min(1)] [SerializeField] private float _stopJumpFactor = 2.5f; }
Ondoren, pertsonaia ezkerrera eta eskuinera mugitzeko gaitasuna ezarriko dugu. Mugimendu-botoia sakatuta edukitzean, pertsonaiak zehaztutako mugimendu-abiadura mantendu behar du oztopoak gorabehera. Hori lortzeko, aldagai bat gehituko dugu scriptean uneko mugimendu-abiadura gainazalean gordetzeko (edo, besterik gabe, horizontalean pertsonaia airean dagoenean):
// CharacterController.cs private float _locomotionVelocity;
CharacterBody
osagaian, abiadura hau ezartzeko metodo bat aurkeztuko dugu:
// CharacterBody.cs public void SetLocomotionVelocity(float locomotionVelocity) { Velocity = new Vector2(locomotionVelocity, _velocity.y); }
Gure jokoa ez denez gainazal maldarik agertzen, metodo hau nahiko erraza da. Eszenatoki konplexuagoetan, gorputzaren egoera eta gainazaleko malda kontuan hartu beharko genituzke. Oraingoz, abiaduraren osagai bertikala mantentzen dugu, x
koordenatu horizontala soilik aldatzen dugun bitartean.
Ondoren, balio hau Update
metodoan ezarriko dugu fotograma guztietan:
// CharacterController.cs private void Update() { _characterBody.SetLocomotionVelocity(_locomotionVelocity); }
Move
Sarrera Ekintzaren seinaleak kudeatzeko metodo bat definituko dugu:
// CharacterController.cs public void OnMove(InputAction.CallbackContext context) { var value = context.ReadValue<Vector2>(); _locomotionVelocity = value.x * _speed; }
Move
ekintza Vector2
gisa definitzen denez, testuinguruak balio bektorial bat emango du sakatzen edo askatzen diren teklaren arabera. Adibidez, D
tekla sakatzean OnMove
metodoak bektorea jasoko du (1, 0). D
eta W
biak aldi berean sakatuz gero (1, 1) izango da. Tekla guztiak askatuz gero, OnMove
abiaraziko da (0, 0) balioarekin.
A
gakoarentzat, bektorea (-1, 0) izango da. OnMove
metodoan, jasotako bektorearen osagai horizontala hartu eta zehaztutako mugimendu-abiaduraz biderkatuko dugu, _speed
.
Lehenik eta behin, CharacterBody
osagaia jauziak maneiatzen irakatsi behar diogu. Horretarako, jauziaren ardura duen metodo bat gehituko dugu:
// CharacterBody.cs public void Jump(float jumpSpeed) { Velocity = new Vector2(_velocity.x, jumpSpeed); State = CharacterState.Airborne; }
Gure kasuan, metodo hau zuzena da: abiadura bertikala ezartzen du eta berehala aldatzen du pertsonaiaren egoera Airborne
.
Ondoren, pertsonaiak salto egin behar duen abiadura zehaztu behar dugu. Dagoeneko definitu dugu jauziaren altuera eta badakigu grabitateak etengabe eragiten duela gorputzean. Horren arabera, hasierako jauzi-abiadura kalkula daiteke formula hau erabiliz:
Non h jauziaren altuera den eta g grabitazio-azelerazioa den. CharacterBody
osagaian dagoen grabitate biderkatzailea ere kontuan hartuko dugu. Eremu berri bat gehituko dugu hasierako jauzi-abiadura definitzeko eta honela kalkulatuko dugu:
// CharacterController.cs private float _jumpSpeed; private void Awake() { _jumpSpeed = Mathf.Sqrt(2 * Physics2D.gravity.magnitude * _characterBody.GravityFactor * _jumpHeight); }
Beste eremu bat beharko dugu pertsonaia unean jauzi egiten ari den ala ez jarraitzeko, beraz, jauzi-abiadura dagokion momentuan mugatu ahal izango dugu.
Gainera, jokalariak lurreratu arte salto-botoiari eusten badio, guk geuk berrezarri beharko genuke bandera hau. Update
metodoan egingo da:
// CharacterController.cs private bool _isJumping; private void Update() { if (_characterBody.State == CharacterState.Grounded) { _isJumping = false; } //... }
Orain, idatzi dezagun Jump
ekintza kudeatzeko metodoa:
// CharacterController.cs public void OnJump(InputAction.CallbackContext context) { if (context.started) { Jump(); } else if (context.canceled) { StopJumping(); } }
Jump
ekintza botoi bat denez, testuingurutik zehaztu dezakegu botoia sakatzea hasi ( context.started
) ala amaitu ( context.canceled
). Horretan oinarrituta, saltoa hasi edo gelditzen dugu.
Hona hemen saltoa exekutatzeko metodoa:
// CharacterController.cs private void Jump() { if (_characterBody.State == CharacterState.Grounded) { _isJumping = true; _characterBody.Jump(_jumpSpeed); } }
Salto egin aurretik, pertsonaia lurrean dagoen egiaztatzen dugu. Hala bada, _isJumping
bandera ezarriko dugu eta gorputza jauzi egingo dugu _jumpSpeed
arekin.
Orain, inplementatu dezagun stop-jauzi portaera:
// CharacterController.cs private void StopJumping() { var velocity = _characterBody.Velocity; if (_isJumping && velocity.y > 0) { _isJumping = false; _characterBody.Velocity = new Vector2( velocity.x, velocity.y / _stopJumpFactor); } }
_isJumping
bandera aktibatuta badago soilik gelditzen dugu jauzia. Beste baldintza garrantzitsu bat pertsonaia gorantz mugitzea da. Horrek erorketa-abiadura mugatzea eragozten du jauzi-botoia beherantz mugitzean askatzen bada. Baldintza guztiak betetzen badira, _isJumping
bandera berrezartzen dugu eta abiadura bertikala _stopJumpFactor
faktore batean murrizten dugu.
Osagai guztiak prest daudenez, gehitu PlayerInput
eta CharacterController
osagaiak eszenako Captain objektuari. Ziurtatu sortu dugun CharacterController
osagaia hautatzen duzula, ez 3D karaktereak kontrolatzeko diseinatutako Unity osagai estandarra.
CharacterController
i, esleitu lehendik dagoen CharacterBody
osagaia karaktereari. PlayerInput
erako, ezarri aurretik sortutako Kontrolak Actions
eremuan.
Ondoren, konfiguratu PlayerInput osagaia CharacterController-etik metodo egokiak deitzeko. Zabaldu Gertaerak eta Pertsonaiak atalak editorean, eta lotu dagozkien metodoak Mugitu eta Salto ekintzekin.
Orain, dena prest dago jokoa exekutatzeko eta konfiguratutako osagai guztiek elkarrekin nola funtzionatzen duten probatzeko.
Orain, kamerak pertsonaia joan den lekuan jarraitu behar dugu. Unity-k kamera kudeatzeko tresna indartsua eskaintzen du — Cinemachine .
Cinemachine Unity-ko kamera kontrolatzeko soluzio iraultzaile bat da, garatzaileei gaitasun ugari eskaintzen dizkiena, joko-beharretara egokitzen diren kamera-sistema dinamiko eta ondo sintonizatuak sortzeko. Tresna honek kameraren teknika konplexuak ezartzea errazten du, hala nola pertsonaien jarraipena, fokuaren doikuntza automatikoa eta askoz gehiago, eszena bakoitzari bizitasuna eta aberastasuna gehituz.
Lehenik eta behin, kokatu Kamera Nagusia objektua eszenan, eta gehitu CinemachineBrain
osagaia.
Ondoren, sortu objektu berri bat CaptainCamera izeneko eszenan. Hauxe izango da kapitaina jarraitzen duen kamera, kameralari profesional bat bezala. Gehitu CinemachineVirtualCamera
osagaia. Ezarri Jarraitu eremua kapitainari, aukeratu Framing Transposer Gorputza eremurako eta ezarri Lens Ortho Size parametroa 4.
Gainera, beste osagai bat beharko dugu kameraren desplazamendua pertsonaiarekiko definitzeko — CinemachineCameraOffset
. Ezarri Y balioa 1,5ean eta Z balioa -15ean.
Orain, proba dezagun kamerak nola jarraitzen duen gure izaera.
Nahiko ondo atera dela uste dut. Kamerak tarteka totelka egiten duela ohartu nintzen. Hau konpontzeko, Kamera Nagusiaren objektuaren Blend Update Method eremuan FixedUpdate ezarri dut.
Probatu ditzagun eguneratutako mekanika. Saiatu korrika eta etengabe salto egiten. Jokalari esperientziadunek jauziak ez direla beti erregistratzen ohartuko dira. Joko gehienetan, hau ez da arazo bat.
Bihurtzen da zaila dela lurreratzeko ordu zehatza aurreikustea berriro salto botoia sakatzeko. Jokoa barkagarriagoa egin behar dugu, jokalariei lurreratu aurretik salto apur bat sakatu eta pertsonaia lurreratzean berehala salto egin dezaten. Jokalariek ohituta daudenarekin bat dator jokabide hau.
Hau ezartzeko, aldagai berri bat sartuko dugu, _jumpActionTime
, aukera sortzen bada salto bat abiarazi daitekeen denbora-leihoa adierazten duena.
// CharacterController.cs [Min(0)] [SerializeField] private float _jumpActionTime = 0.1f;
_jumpActionEndTime
eremu bat gehitu dut, salto ekintzaren leihoaren amaiera markatzen duena. Beste era batera esanda, _jumpActionEndTime
iritsi arte, pertsonaiak salto egingo du aukera sortzen bada. Eguneratu dezagun Jump
ekintza-kudeatzailea ere.
// CharacterController.cs private float _jumpActionEndTime; public void OnJump(InputAction.CallbackContext context) { if (context.started) { if (_characterBody.State == CharacterState.Grounded) { Jump(); } else { _jumpActionEndTime = Time.unscaledTime + _jumpActionTime; } } else if (context.canceled) { StopJumping(); } }
Salto botoia sakatzen denean, pertsonaia lurrean badago, berehala egiten dute salto. Bestela, saltoa oraindik egin daitekeen denbora-leihoa gordetzen dugu.
Kendu dezagun Grounded
egoeraren egiaztapena Jump
metodotik bertatik.
// CharacterController.cs private void Jump() { _isJumping = true; _characterBody.Jump(_jumpSpeed); }
Stop-jauzi metodoa ere egokituko dugu. Botoia lurreratu aurretik askatu bazen, ez litzateke jauzirik gertatu behar, beraz, _jumpActionEndTime
berrezarri dugu.
// CharacterController.cs private void StopJumping() { _jumpActionEndTime = 0; //... }
Noiz egiaztatu behar dugu pertsonaia lurreratu dela eta salto bat eragin? CharacterBody
egoera FixedUpdate
-n prozesatzen da, ekintza prozesatzea geroago gertatzen den bitartean. Update
edo FixedUpdate
den kontuan hartu gabe, lurreratzea eta jauziaren artean fotograma bateko atzerapena gerta daiteke, eta hori nabaria da.
StateChanged
gertaera bat gehituko dugu CharacterBody
n lurreratzeari berehala erantzuteko. Lehenengo argumentua aurreko egoera izango da, eta bigarrena uneko egoera.
// CharacterBody.cs public event Action<CharacterState, CharacterState> StateChanged;
Egoeraren kudeaketa egokituko dugu egoera aldaketa gertaera abiarazteko eta FixedUpdate
berridatziko dugu.
// CharacterBody.cs [field: SerializeField] private CharacterState _state; public CharacterState State { get => _state; private set { if (_state != value) { var previousState = _state; _state = value; StateChanged?.Invoke(previousState, value); } } }
surfaceHit
nola kudeatzen den ere findu nuen FixedUpdate
-n.
// CharacterBody.cs private void FixedUpdate() { //... if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; Velocity = ClipVector(_velocity, surfaceHit.normal); if (surfaceHit.normal.y >= _minGroundVertical) { State = CharacterState.Grounded; return; } } State = CharacterState.Airborne; }
CharacterController
-en, StateChanged
gertaerara harpidetuko gara eta kudeatzaile bat gehituko dugu.
// CharacterController.cs private void OnEnable() { _characterBody.StateChanged += OnStateChanged; } private void OnDisable() { _characterBody.StateChanged -= OnStateChanged; } private void OnStateChanged(CharacterState previousState, CharacterState state) { if (state == CharacterState.Grounded) { OnGrounded(); } }
Update
Grounded
egoera egiaztatzea kenduko dugu eta OnGrounded
era eramango dugu.
// CharacterController.cs private void Update() { _characterBody.SetLocomotionVelocity(_locomotionVelocity); } private void OnGrounded() { _isJumping = false; }
Orain, gehitu kodea jauzi bat abiarazi behar den egiaztatzeko.
// CharacterController.cs private void OnGrounded() { _isJumping = false; if (_jumpActionEndTime > Time.unscaledTime) { _jumpActionEndTime = 0; Jump(); } }
_jumpActionEndTime
uneko ordua baino handiagoa bada, jauzi botoia duela gutxi sakatu dela esan nahi du, beraz, _jumpActionEndTime
berrezarri eta jauzia egiten dugu.
Orain, saiatu etengabe jauzi egiten pertsonaiarekin. Konturatuko zara salto-botoia sentikorragoa dela eta pertsonaia kontrolatzea leunagoa dela. Hala ere, ikusi nuen zenbait egoeratan, beheko ilustrazioan ageri den izkinan adibidez, Grounded
egoerak atzerapen apur bat jasaten duela, jauzi-katea eten egiten duela.
Honi aurre egiteko, CharacterBody
osagaiko Surface Anchor
eremua 0.05ean ezarri nuen 0.01-en ordez. Balio honek gorputza Grounded
egoeran sartzeko gainazal baterako gutxieneko distantzia adierazten du.
Konturatuko zara gainazal bertikaletatik korrika egiten ari zaren bitartean salto egiten saiatzeak ez duela beti funtzionatzen. Salto botoiak batzuetan ez duela erantzuten sentitu daiteke.
Hau da 2D plataformako karaktere kontrolatzaile bat garatzeko ñabardura bat. Jokalariek jauzi egiteko gaitasuna behar dute jauzi botoia sakatzean berandu samar bada ere. Kontzeptu hau bitxia dirudien arren, plataformako jokalari gehienek nola funtzionatzen dute. Emaitza airea bultzatzen duen pertsonaia bat da, beheko animazioan erakusten den moduan.
Ezar dezagun mekanika hau. Eremu berri bat sartuko dugu denbora-leihoa (segundotan) gordetzeko, zeinetan pertsonaiak oraindik ere salto egin dezakeen Grounded
egoera galdu ondoren.
// CharacterController.cs [Min(0)] [SerializeField] private float _rememberGroundTime = 0.1f;
Beste eremu bat ere gehituko dugu denbora-zigilua gordetzeko, eta ondoren, Grounded
egoera "ahazten" da.
// CharacterController.cs private float _lostGroundTime;
Egoera honen jarraipena egingo da CharacterBody
gertaera erabiliz. OnStateChanged
kudeatzailea egokituko dugu horretarako.
// CharacterController.cs private void OnStateChanged(CharacterState previousState, CharacterState state) { if (state == CharacterState.Grounded) { OnGrounded(); } else if (previousState == CharacterState.Grounded) { _lostGroundTime = Time.unscaledTime + _rememberGroundTime; } }
Garrantzitsua da bereiztea pertsonaiak Grounded
egoera galdu duen nahitako salto baten ondorioz edo beste arrazoi batengatik. Dagoeneko dugu _isJumping
bandera, StopJumping
deitzen den bakoitzean desgaituta dagoena ekintza erredundanteak saihesteko.
Beste bandera bat ez sartzea erabaki nuen, jauzi erredundanteak bertan behera uzteak ez duelako jokatzeko eraginik. Anima zaitez esperimentatzen. _isJumping
bandera orain pertsonaia salto egin ondoren lurreratzen denean bakarrik garbituko da. Eguneratu dezagun kodea horren arabera.
// CharacterController.cs private void StopJumping() { _jumpActionEndTime = 0; var velocity = _characterBody.Velocity; if (_isJumping && velocity.y > 0) { _characterBody.Velocity = new Vector2( velocity.x, velocity.y / _stopJumpFactor); } }
Azkenik, OnJump
metodoa berrikusiko dugu.
// CharacterController.cs public void OnJump(InputAction.CallbackContext context) { if (context.started) { if (_characterBody.State == CharacterState.Grounded || (!_isJumping && _lostGroundTime > Time.unscaledTime)) { Jump(); } else { _jumpActionEndTime = Time.unscaledTime + _jumpActionTime; } } else if (context.canceled) { StopJumping(); } }
Orain, gainazal bertikaletatik jauzi egiteak ez du jokatzeko erritmoa apurtzen eta askoz naturalagoa sentitzen da, itxurazko absurdua izan arren. Pertsonaiak literalki airetik bota dezake, logikoa dirudiena baino urrunago joanez. Baina hori da gure plataformarako behar dena.
Azken ukitua pertsonaia mugimenduaren norabideari aurre egitea da. Modurik errazenean inplementatuko dugu, pertsonaiaren eskala x ardatzean zehar aldatuz. Balio negatiboa ezartzeak gure kapitainak kontrako noranzkoari begira jarriko du.
Lehenik eta behin, gorde dezagun jatorrizko eskala 1etik desberdina bada.
// CharacterController.cs public class CharacterController : MonoBehaviour { //... private Vector3 _originalScale; private void Awake() { //... _originalScale = transform.localScale; } }
Orain, ezkerrera edo eskuinera mugitzean, eskala positiboa edo negatiboa aplikatuko dugu.
// CharacterController.cs public class CharacterController : MonoBehaviour { public void OnMove(InputAction.CallbackContext context) { //... // Change character's direction. if (value.x != 0) { var scale = _originalScale; scale.x = value.x > 0 ? _originalScale.x : -_originalScale.x; transform.localScale = scale; } } }
Proba dezagun emaitza.
Artikulu hau nahiko zehatza izan zen, baina pertsonaien kontrolaren funtsezko alderdi guztiak 2D plataforma batean estaltzea lortu genuen. Oroigarri gisa, azken emaitza biltegiko " Caracter Controller " adarrean ikus dezakezu.
Hau eta aurreko artikulua lagungarri gustatu bazaizu edo gustatu bazaizu, eskertuko nizuke GitHub-en atsegin eta izarrak. Ez izan zalantzarik eta jar zaitez harremanetan arazoren bat aurkitzen baduzu edo akatsak aurkitzen badituzu. Eskerrik asko zure arretagatik!