A színfalak mögött

G-Ball · sztrovacsek

Programok készítésnél nem mindig sikerül elsőre tökéletes kódot írni (sokadszorra sem, de most nem ez a lényeg), de ami nem romlott el azt nem is kell megjavítani itt jön a képbe az optimalizálás. Kétféleképpen is optimalizálhatunk, az egyik a gépnek segít, a másik pedig a fejlesztőnek.


A gépnek úgy segíthetünk, ha nem írunk felesleges kódot, illetve nem hajtunk végre utasításokat, ha nincs rájuk szükség. Jó példa erre az eredményjelző. Megoldhatnánk úgy is, hogy minden egyes ciklusban kérje le az eredményt (ami akár több ezer is lehet másodpercenként):

func _process(delta):
    ScoreLabel.text = str(Global.left_score + " - " + Global.right_score)

Ez működik is, de nem sok értelme van, mivel az eredmény csak akkor változik, ha a labda bekerül valamelyik kapuba. Helyette sokkal észszerűbb, ha csak a gól után frissítjük a kiírást:

func _on_Goal_area_entered(area):
    if side == left:
        Global.left_score += 1
    if side == right:
        Global.right_score += 1
    Global.ScoreLabel.text = str(Global.left_score + " - " + Global.right_score)

A végeredményt tekintve mindkét kód ugyanazt a célt éri el, de működésükben teljesen különbözőek. Gyors prototípus készítésnél gyakran egyszerűbb az első verziót használni (ez nyilván, egy nagyon leegyszerűsített példa volt), de ha sok ilyen marad a végleges kódban, akkor az jelentősen meg tudja emelni feleslegesen a gépigényt.


A fejlesztőt segítő optimalizálások nem feltétlenül vannak kihatással a játék futására, de sok idegeskedéstől kímélhetnek meg. A legegyszerűbb ilyen dolog, hogy adjunk leíró neveket a változóknak (NagyBetűvel kezdődnek az objektumok nevei, kis_betűvel pedig a változók nevei):

### beszédes változó nevek
func _physics_process(delta):
    if Global.Player:
        PlayerInfo.text = str(Global.Player.player_number, " - ", Global.Player.player_name)
        ChargePower.rect_size.x = (Global.Ball.power * 250) - 250
    else:
        PlayerInfo.text = ""


### ☠ ď̸̙͈͍̥̭é̷͔͇̘̲͚̆̾̿͘m̶͈̬̣̋̄̉͂̕͜on̴̳̚͜ī̸̪͔̎͛͜͝d̵̢̹̯͌͗͜ȩ̴̖̭́̑ͅz̴͎̉̿͗ȩ̸̤̰̫̦́͐͌̊̎̈́s̴̝͙̝͂̃̆ ⛧
func _physics_process(delta):
    if Global.KinematicBody2D:
        Label.text = str(Global.KinematicBody2D.var1, " - ", Global.KinematicBody2D.var2)
        ColorRectangle.rect_size.x = (Global.RigidBody2D.var1 * 250) - 250
    else:
        Label.text = ""

A fenti két kódrészlet a gép szempontjából ugyanaz, de a fejlesztő szempontjából már egyáltalán nem. A másodikat ember legyen a talpán, aki ránézésre megérti, hogy pontosan mire szolgál. Tovább rontja a helyzetet, hogy 2 var1 nevű változó is van, de mindkettő más értéket takar, az egyik a játékoshoz tartozik, a másik pedig a labdához.


Egy másik nagyon egyszerű mód a kód olvashatóbbá tételére a DRY (Don't Repeat Yourself, azaz ne ismételd önmagad) módszer. Mint a nevéből is sejthető ez a duplikált kód eltüntetését szorgalmazza. Vegyük az alábbi példát:

func auto_select_new_player():
    var bp = Global.Ball.global_position
    var player_ids = get_tree().get_nodes_in_group("player")
    Global.nearest_player = player_ids[0]
    for player_id in player_ids:
        if player_id.global_position.distance_to(dist) < Global.nearest_player.global_position.distance_to(bp):
            Global.nearest_player = player_id

func manual_select_new_player():
    var mp = get_global_mouse_position()
    var player_ids = get_tree().get_nodes_in_group("player")
    Global.nearest_player = player_ids[0]
    for player_id in player_ids:
        if player_id.global_position.distance_to(dist) < Global.nearest_player.global_position.distance_to(mp):
            Global.nearest_player = player_id

A két kód szinte teljesen ugyanaz, az egyetlen különbség, hogy a labdától (bp), vagy az egértől (mp) való távolságot vizsgáljuk. Létrehozhatunk tehát egy új függvényt, amit a másik 2 használhat a saját változójával:

func dist_check(dist_from):
    var player_ids = get_tree().get_nodes_in_group("player")
    Global.nearest_player = player_ids[0]
    for player_id in player_ids:
        if player_id.global_position.distance_to(dist) < Global.nearest_player.global_position.distance_to(dist_from):
            Global.nearest_player = player_id

func auto_select_new_player():
    var bp = Global.Ball.global_position
    dist_check(bp)

func manual_select_new_player():
    var mp = get_global_mouse_position()
    dist_check(mp)

Ennek a megoldásnak több előnye is van: egyrészt rövidebb és átláthatóbb lesz tőle a kód, másrészt, ha valamit változtatni akarunk a függvényben, akkor nem kell minden egyes előfordulásnál külön megtenni, hanem elég csak egyszer, ezzel is csökkentve a hibalehetőségek számát. 


Még számos egyéb módja lehet az optimalizációnak, ezekről majd a későbbi részekben lesz szó.