Jatkoin peliohjelmointiharjoituksia ja olen tutustunut oppaissa ja videoissa usein mainittuun finite-state machine (FSM) ideaan. Useimmissa toteutuksissa ajatuksena on se, että jokainen tila kirjoitetaan erilliseen tiedostoon. Sen päälle on sitten jonkinlainen tilojen kantatoteutus sekä varsinainen state machine, joka hanskaa tilasta toiseen siirtymisen.

Kaunis ajatus on se, että kun koodin pilkkoo tällaisiin pieniin palasiin, se tekee tilojen hallinnasta helpompaa ja jokaisen tilan tarvitsema koodi on selkeästi kapseloituna.

Muutin edellisen harjoituksen koodin noudattamaan FSM:ää ja täytyy kyllä todeta, että minusta ei tullut fani. Ymmärrän hyvin, että tällainen toteutus on paikallaan kun puhutaan valtavista peleistä, jossa erilaisia tiloja on kymmeniä ja pelissä on hahmoja, joilla voi olla erilainen yhdistelmä tiloja. Tälle on epäilemättä paikkansa.

Ainakin tämän yhden kokemuksen perusteella asioiden pilkkominen näin pieniin palasiin toi vaan tarpeetonta kompleksisuutta ja abstrahointia. Koodirivien määrä räjähti käsiin enkä kyllä voi käsi sydämellä sanoa, että toteutus on jotenkin helpompi ymmärtää tai ylläpitää. Kun yhtälöön lisää sen, että nämä tilat ovat joka tapauksessa varsin tiukasti sidottuja hahmoon, joka niitä käyttää, niin saatu hyöty tuntui aika olemattomalta.

Voi toki olla, että toteutin FSM:n ihan päin persettä, mutta tykkäsin itse paljon enemmän toteutuksesta, jossa tilakone oli rakennettu ihan vaan enumeilla ja selkeästi jäsennellyllä koodilla.

#ohjelmointi #peliohjelmointi

@saaste Godotissa GDscriptillä omasta mielestä FSM on vähän monimutkaisempi tehdä kuin esim. C# Unityssä. Luokka tuki vähän rajallisempaa kuin muissa kielissä.

Ite tykkään tuosta paloittelusta niin paljon, että yleensä jaan vielä State:n jokaisen actionin vielä omaan osaan :D

@mika GDScriptin rajoitukset tekee tuosta kieltämättä vielä sotkuisempaa. Siinä kun ei ole edes kunnollista namespace tukea. Jotain hackeja siihen näyttäisi olevan ja tietysti luokat voi nimetä tyyliin Player_State_Idle ja Enemy_Goomba_State_Idle, mutta vähän kökköähän tuo on 😄

Kun tykkäät paloitella asiat pieniin palasiin niin pitää kysyä, että miten olet hanskannut sen, että tilan (tai actionin) käsittelijä tarvitsee kuitenkin pelistä riippuen kaikenlaista tietoa omistajastaan. Jo pelkästään tuossa omassa harjoituksessa minun piti kuljettaa sinne animaatiot, hitboxit sekä collision shapet.

Itse päädyin ratkaisuun, jossa sen omistajan _ready() metodissa kutsutaan esimerkiksi:

state_manager.init(animation, hitbox, collision_shape...)

Ja sitten state_manager puolestaan viskoo nuo tiedot jokaiselle state-luokalle. Niillä on kyllä tiedossa myös state manager, joten ne voisi tallentaa vain siihen, mutta koin selkeämmäksi, että tilan käsittelijällä on itsellään tiedossa kaikki mitä se tarvitsee.

@saaste TLDR: Hahmot koostuu komponenteista niin tarvii siirrellä vähemmän dataa.

Ei oo esimerkkiä GDScripitllä kun nykyisesssä pelissä en käytä FSM (,koska enemmän ui pohjainen peli). Monogamelle mulla oli tämmönen ratkaisu.

Jokainen hahmo on GameObject ja koostuun komponenteista (healthcomponent,inputcomponent jne). Joka hahmolla on myös StateComponent mikä on vähän niin kuin se hahmon päähallitsija.

jatkuu

@saaste Mun arkkitehtuurin pääosa on Actionit (https://codeberg.org/PaahtimoGames/Mushy-Score/src/branch/main/GameObjectSystem/EE.GameObjectSystem/EEAction.cs). Nää on yksinkertaisia luokkia mitkä tekee jonkun jutun. Esim. tässä health muutos: (https://codeberg.org/PaahtimoGames/Mushy-Score/src/commit/8e5b6f2d546d2ed2f956cb8d9d89f00a30171a57/ResourceSystem/EE.ResourceSystem.Actions/ChangeResourceAction.cs). Mikä tahansa GameObject voi kutsua noita actioneitä, jos sillä on tarvittavat komponentit.

Jokanen State sitten koostuu näistä Actioneistä, joiden avulla määritellään miten State toimii ja millä ehdoilla siirrytään muihin. Voin tosta ite StateMachinen toiminnastakin selittää tarkemmin, jos haluat.

@saaste Veikkaan, että sun tapauksessa atm. hahmon kaikki logiikka on vielä yhdessä luokassa (player.gd ja goomba.gd)? Eli ennen kuin pääsee hyödyntää kunnolla FSM niin todennäkösesti paras ois jakaa hahmot eka komponentteihin. Esim. siirtää health logiikan omaan luokkaan (tai nodeen).Yksinkertaisimman esimerkin löysin ois tää: https://www.youtube.com/watch?v=74y6zWZfQKk
How You Can Easily Make Your Code Simpler in Godot 4

YouTube
@saaste Silloin kuin suurin osa logiikasta tapahtuu komponentin sisällä niin ite Stateihin tarvii siirtää vaan komponentit, eikä muuta tietoa. Esim. jos on Idle State mikä tekee idle animaatiota niin se tarvitsee vain animaatiokomponentin ja kutsuu animaatiokomponentin playanimation(). Animaationkomponentti sitten hoitaa itse animaation logiikan.

@mika Kiitti vastauksesta! Jotain tämänkaltaista ajattelinkin. Täytyy tutkailla tuota arkkitehtuuria töiden jälkeen ihan ajatuksen kanssa.

Ajattelin seuraavana harjoituksena kokeilla tehdä yhtä kevyttä lautapeliä digitaaliseen muotoon. Katsotaan olisiko siellä jostain tällaisesta hyötyä ☺️