Choď na pozíciu a Drž formáciu
Analýza
Už v minulom roku bol pokus zapracovať držanie hráčov vo formácii. Stopy po tom sa nachádzali v kóde konkrétne na týchto miestach:
- scripts/plan/PlanCreateFormation.rb – plánovač volajúci high skill GoTo
- scripts/high_skills/GoTo.rb – high skill volajúci metódy v TacticalInfo a pomocou low skillov zabezpečujúci dopravenie hráčov na pozíciu vo formácii
- sk.fiit.jim.agent.models.TacticalInfo.java s metódami:
- setMyFormPosition() – výpočet súradníc pozícií hráčov vo formácii
- getFormPosition() – získanie pozície vo formácii pre hráča
- isInPositionArea() – zisťovanie, či je hráč blízko svojej pozície vo formácii
- isOnPosition() – zisťovanie, či je hráč na pozícii vo formácii
Plánovač „PlanCreateFormation“ mal však v sebe chybu, ktorá znemožňovala využitie formácií.
Návrh
Rozhodli sme sa upraviť tieto existujúce kódy do funkčnej podoby, aby sme ich vedeli používať. Najprv bolo potrebné opraviť existujúci plánovač, odskúsať ako to celé funguje a nakoniec spraviť použiteľným high skill, ktorý by mohol využívať akýkoľvek plánovač.
Keďže high skill „GoTo“ slúžil len na dostatie sa hráča na pozíciu formácie, pre širšie a možno aj neskoršie využitie sme sa rozhodli z neho spraviť získať dva určité prvky. Prvým prvkom by bol high skill „GoToPosition“, ktorý by po zavolaní s parametrom súradníc umožnil presun hráča na tú pozíciu. Druhým prvkom by bola trieda poskytujúca metódu, ktorá by s využitím „GoToPosition“ poslala hráča na pozíciu vo formácii. Tento druhý prvok sme nazvali „FormationHelper“ s metódou „getHighSkillToGoToFormation“, ktorá vracia high skill „GoToPosition“ nastavený na chod do formácie.
Neskôr sme zistili, že počas vykonávania high skillu sa môže stať, že isté podmienky pri ktorých bol high skill zavolaný už neplatia a vykonávanie high skillu by bolo potrebné zrušiť. Zaviedli sme preto ďalší parameter, ktorým si high skill pravidelne kontroluje, či je stále validný. Pokiaľ high skill vyhodnotí, že už nespĺňa podmienky validity, tak pomocou „return nil“ odovzdá riadenie plánovaču.
Implemetácia
Najprv sme naimplementovali high skill „GoToPosition“. Jeho konštruktor má dva už spomínané parametre: globálna pozícia kam sa má hráč presunúť a proces valídnosti. Keď high skill volá svoju metódu „pickLowSkill“, najskôr zavolá proces valídnosti, ktorý skontroluje, či má zmysel high skill ďalej vykonávať. Ďalej nasleduje zrelativizovanie globálnej pozície na pozíciu vzhľadom na hráča pomocou Java triedy „AgentModel“ a jej metódy „reletivize“. Nasledujú volania jednotlivých low skillov zabezpečujúcich pohyb. Tu je ukážka začiatku metódy „pickLowSkill“:
def pickLowSkill if not @validity_proc.call return nil end if @target_position_global == nil return nil else @agentModel = Java::sk.fiit.jim.agent.models.AgentModel.getInstance @target_position = @agentModel.relativize(@target_position_global) end
Ďalej sme naimplementovali tzv. helper triedu „FormationHelper“ so statickou metódou „getHighSkillToGoToFormation“. Táto trieda umožní vrátiť plánovaču high skill, ktorý zabezpečí dopravenie hráča na pozíciu vo formácii. Jej metóda potrebuje ako parameter len proces valídnosti. V tele metódy sa potom vypočíta pozícia vo formácii a zavolá sa „GoToPosition“:
class FormationHelper # @param validity_proc - process for calling method from plan to check # if GoToPosition is still valid or not def FormationHelper.getHighSkillToGoToFormation validity_proc tacticalInfo = Java::sk.fiit.jim.agent.models.TacticalInfo.getInstance target_position = tacticalInfo.getFormPositionGlobal() return GoToPosition.new(target_position, validity_proc) end end
Príklad volania v plánovači s procesom validity takým, čo kontroluje či hráč vidí loptu, môže byť takýto:
@plan << FormationHelper.getHighSkillToGoToFormation(Proc.new{see_ball?})
Keďže v „GoToPosition“ sa globálne súradnice relativizujú, potrebovali sme z Java triedy „TacticalInfo“ dostávať vypočítané pozície do formácie v globálnom tvare. Preto sme do „TacticalInfo“ pridali metódu „getFormPositionGlobal“:
public Vector3D getFormPositionGlobal(){ setMyFormPosition(); return pos; }
Čo sa týka Java metódy „setMyFormPosition“, zistili sme, že je v nej potrebné prepočítavať súradnice vzhľadom na stranu tímu. Čiže ak má hráč pravú stranu tímu, trebalo mu v súradniciach invertovať x-ovú a y-ovú os (teda násobiť tie časti súradníc číslom -1). Taktiež bolo potrebné invertovať aj pozíciu lopty, ktorá sa nachádzala v podmienkach metódy. V metóde nižšie je vidieť, že súradnice jednotlivých pozícií vo formácii sú napevno zadané hráčovi podľa jeho poradového čísla. V budúcnosti by to bolo potrebné prerobiť napríklad tak, že hráč idúci do formácie obsadí niektorú zatiaľ neobsadenú pozíciu.
public void setMyFormPosition(){ PositionInFormation = new Vector3D[7]; double ball_pos = WorldModel.getInstance().getBall().getPosition().getX(); int flip = 1; if (AgentInfo.side == Side.RIGHT) { flip = -1; ball_pos = ball_pos * flip; } if(ball_pos < 5.5 && ball_pos > -4.5){ PositionInFormation[0]=Vector3D.cartesian((ball_pos - 5) * flip, 0 * flip , 0); PositionInFormation[1]=Vector3D.cartesian((ball_pos - 1) * flip, 0 * flip, 0); PositionInFormation[2]=Vector3D.cartesian((ball_pos + 4) * flip, 0 * flip, 0); PositionInFormation[3]=Vector3D.cartesian((ball_pos - 5) * flip, -3 * flip, 0); PositionInFormation[4]=Vector3D.cartesian((ball_pos - 5) * flip, 3 * flip, 0); PositionInFormation[5]=Vector3D.cartesian((ball_pos - 1) * flip, -3 * flip, 0); PositionInFormation[6]=Vector3D.cartesian((ball_pos - 1) * flip, +3 * flip, 0); }else{ if(ball_pos <= 0){ PositionInFormation[0]=Vector3D.cartesian(-9 * flip, 0 * flip, 0); PositionInFormation[1]=Vector3D.cartesian(-5 * flip, 0 * flip, 0); PositionInFormation[2]=Vector3D.cartesian(0 * flip, 0 * flip, 0); PositionInFormation[3]=Vector3D.cartesian(-9 * flip, -4 * flip, 0); PositionInFormation[4]=Vector3D.cartesian(-9 * flip, 4 * flip, 0); PositionInFormation[5]=Vector3D.cartesian(-5 * flip, -4 * flip, 0); PositionInFormation[6]=Vector3D.cartesian(-5 * flip, +4 * flip, 0); } else { PositionInFormation[0]=Vector3D.cartesian(0 * flip, 0 * flip, 0); PositionInFormation[1]=Vector3D.cartesian(4 * flip, 0 * flip, 0); PositionInFormation[2]=Vector3D.cartesian(8 * flip, 0 * flip, 0); PositionInFormation[3]=Vector3D.cartesian(0 * flip, -4 * flip, 0); PositionInFormation[4]=Vector3D.cartesian(0 * flip, 4 * flip, 0); PositionInFormation[5]=Vector3D.cartesian(4 * flip, -4 * flip, 0); PositionInFormation[6]=Vector3D.cartesian(4 * flip, +4 * flip, 0); } } for(int i=0;i < 7;i++){ if(AgentInfo.playerId == i+1) { pos = PositionInFormation[i]; } } }