Ez a projekt egy kétdimenziós robotfoci szimuláció: a fizikai világ és az időlépés C nyelven fut, a játékosok viselkedése pedig Lua taktikai szkriptekben van leírva. Minden robothoz külön Lua állomány (tactics.lua + tacapi.lua) fut; a C motor a pályán lévő testeket (robotok + labda) lépteti, és egy központi c_api függvényen keresztül adja vissza a pozíciókat, illetve fogadja a mozgás- és rúgásparancsokat.
Összkép:
| Réteg | Szerep |
|---|---|
C (game.c, main.c) |
Meccsállapot, idő, fizikai integráció, Lua VM-ek, c_api hívások |
Lua taktika (api_repo/tactics/) |
OnInit / OnUpdate: szerepek, célzás, mikor rúgjunk |
Megjelenítés (render.c) |
GLFW ablak, OpenGL 2.x-szerű rajzolás: pálya, robotok, labda |
Megjegyzés: A közös fejléc
include/types.h. Asrc/types.cnem tartalmazza újra a típusdefiníciókat — csak a vektor-matematikai segédfüggvényeket implementálja.
vec2_t: kétdimenziós vektor (x,y) — pozíció vagy sebesség komponensei.body_t: fizikai test —center(pozíció) ésspeed(sebesség). A labda és a robotok ugyanezt a struktúrát használják a mozgáshoz.robotdata_t: robot metaadatai — csapat, id,body_t cs, tájolás, utolsó ütközés ideje, saját Lua állapot (lua_State *L).teamdata_t/matchdata_t: csapatok, labda, bedobás, félidő, utolsó rúgás időbélyege,MatchStateenumeráció.
Labda vs. robot a memóriában: a labda nem külön „entity típus” enumként van tárolva a struktúrában; a match->ball egy body_t, a robotok pedig match->team[t].robot[r].cs alatt. A megkülönböztetés indexeléssel történik a fizikai ciklusban (lásd game.c).
vec_length: vektor hossza (\sqrt{x^2 + y^2}).vec_norm: egységvektor (normalizálás) — nullvektor esetén{0,0}visszaadása, hogy elkerüljük a nullával való osztást.
Ezeket főleg a game_update fizikai része használja (pl. lépés irányának normalizálása az ütközésnél).
lua_api: a Luatacapi.luaáltal hívott C központi kapu. Az első egész argumentum a parancskód (pl.10csapattárs pozíció,12labda,20mozgás,21rúgás).game_update: minden képkockán:- kiszámolja a
deltaTimeértéket; - lefuttatja az összes robot
OnUpdate(dt)Lua függvényét; - frissíti a pozíciókat a sebesség alapján.
- kiszámolja a
A fizikai rész egyszerűsített: a külső ciklus végigmegy minden testen (összes robot + labda). A belső ciklus csak a robotindexeken (b < TEAMS*PLAYERS) megy végig.
- Robot–robot: ha két robot előre vetített pozíciója túl közel van egymáshoz (kb.
2 × MARKER_RADIUStávolság), a lépésvektor (maxStep) rövidül — ez egy enyhe átfedés-elkerülés, nem teljes rigid-body motor. - Labda indexnél (
a == TEAMS*PLAYERS) a feltétela == TEAMS*PLAYERS⇒continuemindenb-re: a belső ciklus nem módosítja a labda lépését robotokkal ebben a blokkban. A labda mozgása főleg sebesség integrálás + súrlódás-szerű csillapítás; a rúgás ac_api21-es ágában állítja a labda sebességét.
Fontos: ez nem „ghosting” a C rétegen (a robotok továbbra is ütköznek egymással ebben a modellben). A lágy, folyékony kitérést a Lua oldalon az elkerülő kormányzás (applyAvoidance a tactics.lua-ban) segíti.
- GLFW ablak létrehozása, OpenGL kontextus (
Render_Create/Render_Destroy). Render_Update: puffercsere, események, háttérszín, viewport — a főciklusban minden frame-ben hívódik.- Koordináta-transzformáció (
_render_vertex): a világkoordinátákat NDC-be (-1…1) képezi az ablak méretével és skálázással. drawField: a pálya elemei — középvonal, középkör, büntetőpontok keresztjei, téglalap kontúrok: pálya széle, kapuk, tizenhatosok, tizenegyes területek (field.hkonstansok alapján).drawMarker: egybody_tkirajzolása rombusz (négy csúcs) alakkal; sebességvektor egy rövid szegmens; opcionálisan „leütött” állapot (fallen) keretezése.
A main.c minden frame-ban: game_update → drawField → robotok és labda drawMarker hívásai.
- A
c_api(...)hívásokat olvasható nevekre fedi (API.Locate.Ball,API.Robot.Move, stb.). - Szögkonvenció: a taktika fokban számol; a
Move/Kickhívás előtt radiánra vált (DEG2RAD), mert a C oldalcos/sinígy várja.
- Kapus (
player == KEEPER_ID):Keeper.Steperedménye →API.Robot.Move, szükség eseténAPI.Robot.Kick. - Mezőny: egy fő támadó (
attackerId— a labdához legközelebbi mezőnyjátékos), hiszterézis (attackerLock) a szerepcsere villogásának csökkentésére. - Támogató (
supporterId): második legközelebbi — előretolt vagy visszazáró célpont. - Labda „burkoló”:
ballObj = { id = "ball", type = "ball", x, y }— kizárólag aAPI.Locate.Ball()C-ből jövő adat másolata; nem másik robot pozíciója.
- Távolság, irányszög (fok), pont–szakasz távolság (passzsáv ellenőrzéshez).
is_valid_ball_object(ballPos): ellenőrzi, hogy a kapott tábla érvényes koordinátákat tartalmaz-e (x,y). Ez nem C-beli entity-ID, hanem védelmi ellenőrzés — a taktika a labdát mindig aBall()API-ból tölti.- Rúgás csak akkor kerül a visszatérési táblába, ha a döntés labda–robot távolságon alapul (edge trigger), nem másik játékos távolságán.
A projektben explicit Utils.isBall() függvény opcionálisan dokumentálható; a gyakorlati szabály:
- A labda pozíciója mindig
API.Locate.Ball()/BallVel(). - A taktika
ballObjnéven adja át a Striker/Keeper moduloknak ugyanazt a táblát. - A Kick csak akkor hívódik, ha a striker/keeper logika
kickAngleDeg+kickForcemezőket ad vissza — ezek a labda közelségéhez kötöttek, nem robot–robot távolsághoz.
A „ghosting” / elkerülés részben Lua-ban van: applyAvoidance közeli játékoshoz képest finomítja a mozgás irányát, hogy kevésbé torlódjanak — nem vált taktikai állapotot és nem indít rúgást önmagában.
game_init: minden robothozluaL_newstate,tacapi.luabetöltése,lua_register(L, "c_api", lua_api).- Globális Lua változók:
team,player,match(light userdata amatchdata_t*-ra). - Minden frame:
OnUpdate(dt)meghívása; a Lua ac_api(opcode, ...)-on keresztül olvas pozíciót / ír sebességet és rúgást.
tactics.lua:applyAvoidance— ha van nagyon közeli másik játékos és a labda nincs „érintési” küszöbön, a kívánt mozgásirányhoz merőleges komponens keveredik → „csúszás” hatás.- A C oldali robot–robot lépéskorrekció ettől független; együtt adják a viselkedést.
- Fordító: Linuxon
gcc, macOS-enclang(lásdmakefile). - Könyvtárak: OpenGL, GLFW, Lua (pl. Homebrew:
brew install glfw lua).
A makefile az operációs rendszer szerint állítja a -L / -I útvonalakat (Linux: /usr/..., macOS: /opt/homebrew/...).
make # Létrehozza a build/sim futtathatót és másolja a Lua fájlokat build/ alá
make run # cd build && ./sim — elindítja az ablakos szimulációt
make clean # build/ mappa törléseAz első fordítás után a build/ könyvtárban lesz a sim és a másolt tactics.lua, tacapi.lua, logic/*.lua fájlok — a futó program innen tölti be őket.
- Lua szintaxis- vagy futásidejű hiba: a konzolra
Error: ...üzenet érkezhet agame_init/game_updatesorán. - macOS-en előfordulhatnak GLFW / windowing figyelmeztetések; ezek nem feltétlenül a taktikai kód hibái.
| Útvonal | Tartalom |
|---|---|
main.c |
Főciklus: init → render → game_update → rajzolás |
include/types.h |
Adattípusok (meccs, robot, labda test) |
src/types.c |
vec_length, vec_norm |
src/game.c |
lua_api, fizika, meccslogika |
src/render.c |
Pálya és testek kirajzolása |
include/field.h |
Pályaméretek, MARKER_RADIUS, stb. |
api_repo/tactics/tactics.lua |
Fő taktika |
api_repo/tactics/tacapi.lua |
C API Lua burkoló |
api_repo/tactics/logic/*.lua |
Kapus / csatár / segédfüggvények |
Dokumentum célja: csapatbemutató és onboarding — a pontos viselkedés mindig a forráskóddal egyeztetendő, ha a szimulációt módosítják.