Upotreba Objective-C u igrama; moje mišljenje

A short Croatian language opinion post on use of Objective-C in games

Dobio sam nedavno pitanje o tome da li se isplati učiti Objective-C (u kontekstu igara).

Moje je mišljenje da Objective-C ima deset puta logičniju internu strukturu nego C++, te da je svojom kombinacijom karakteristika dinamičnih i statičnih jezika izuzetno pogodan za pisanje igara. Primjerice, evo stvaranje kapitalnog svemirskog broda koristeći string, i spremanje istog u SvemirskiBrod:

NSString* nekaj = @"KapitalniSvemirskiBrod"; 
SvemirskiBrod *nekiBrod =  [[NSClassFromString(nekaj) alloc] initWithPosition:CGPointMake(10, 10)];

Ovo je jako dobro jer se ovakva XML specifikacija mape:

<mapa>
  <brod x="10" y="10" tip="KapitalniSvemirskiBrod"/>
</mapa>

može lako učitati s primjerice (ako imamo teoretsku klasu XMLNode i varijablu trenutniNode):

  NSString *tipBroda = [trenutniNode atribut:tip];
  CGPoint pozicija = CGPointMake([trenutniNode atribut:x], [trenutniNode atribut:y]);
  Class klasaBroda = NSClassFromString(tipBroda);
  SvemirskiBrod *brod = [[klasaBroda alloc] initWithPosition:pozicija];

Tu su i druge mogućnosti, tipa lakše pozivanje funkcije nad objektom u nekom mini-skriptnom jeziku koji smisliš (bez razmišljanja o statičkim funkcijama, callbackovima, member-function-pointerima i slično, kao u C i C++).

Pozivanje nepostojeće funkcije (npr [svemirskaStanica udjiUHyperspace];) je samo warning, što znači da se tebi vjeruje da će ta funkcija postojati u toj varijabli. Ako ne postoji, onda će se program skršiti. Postoji provjera da li neka varijabla sadrži neku funkciju.

Ekvivalent std::vectora je NSArray koji unutra može spremati bilo kakve Objective-C objekte. Znači ne moraš templateove slagati i forsirati da se unutra nalaze samo, primjerice, NSStringovi, nego se unutra mogu nalaziti i SvemirskaStanica i SvemirskiBrod i KapitalniSvemirskiBrod.

Uglavnom, iz svega navedenog vidi se da smatram da je Objective-C odličan jezik koji spaja najbolje stvari iz Pythona i C/C++. S obzirom da se u Objective-C može pisati i za Windowse i za Linux koristeći Cocotron i GNUstep, te konačno za Mac i iPhone koristeći Appleove Cocoa i Cocoa Touch, mislim da je to dovoljno portabilan jezik za razvoj igara.

Što se tiče aplikacija, odlično je i za izradu toga, ali relativno manje portabilno ako gledamo naprednije mogućnosti frameworka.

Dakle, da, isplati se :-)

2 thoughts on “Upotreba Objective-C u igrama; moje mišljenje

  1. EmP

    Ovaj prvi primjer s instanciranjem preko imena razreda u stringu, ajd ok, u C++u se mora ručno pisat preslikavanje iz stringa u razred. Al ak ti “klase” u XML-u nisu istog naziva kao u kodu, da li i onda moš proć bez vlasoručnog pisanja factorya?

    Primjer s nepostojećom funkcijom je li-la jer osobno smatram da je loše pozivat funkciju koja ne postoji. Neki editori omogućavaju da si sam stavljaš takve “warninge”, npr. u Eclipse za Javu ističe linije s //TODO komentarom na isti način kao i errore i warninge. Tako da ako ne želiš da ti kompajliranje puca zbog nepostojeće funkcije a ne želiš odmah implementirati tu funkcionalnost, onda samo u prazno tijelo funkcije staviš //TODO.

    Primjer s NSArrayem, da li moraš castat ako elementu niza želiš pristupiti kao objektu nekog razreda? U C++u možeš imat std::vector spremat nutra bilo šta. S druge strane, održivi kod treba biti dizajniran tako da je čim manje specijalnih slučajeva. To znači da bi vector u koji idu različiti tipovi, trebao biti templetiran po nekom baznom tipu (apstraktnom razredu, ne void *) te da se maksimalno koristi dinamički polimorfizam.

    Reply
    1. Ivan Vučica Post author

      1. Naravno da, ako ti se imena klasa ne slažu s onime u data fajlovima, moraš pisati translator iz onoga što je u fajli i obrnuto. No to zaobiđeš tako da — vidvraga — složiš klase i XML na način da imaju ista imena u kodu i fajlama :-)

      Zar je za čuditi se što ako u HTMLu nekom elementu dam id <div id="pero"> poslije trebam u Javascriptu pristupiti tom elementu s document.getElementById('pero')? :-)

      2. Naravno da je loše pozivati funkciju koja nije deklarirana (koja ne postoji). Zato su tu warninzi! Zato ćeš na odgovarajućem mjestu definirati odgovarajući tip podataka i zbog warninga pripaziti da ta funkcija bude deklarirana u @interfaceu. Zatim će te opet warninzi upozoriti da funkcija nije definirana u @implementationu.

      Dok god se držiš best practices, nemaš problema.

      Pa gdje je onda korist? Korist je u tome da imaš generički tip podataka id, efektivno ekvivalentan tipu podataka NSObject koji je bazna klasa za sve objekte u Objective-C. Taj tip podataka namijenjen je upravo tome da se koristi kada ti “garantiraš” da znaš što radiš. Primjerice:

      for (id obj in someArray)
      {
        // znam da su "obj" NSStringovi... ili neki drugi objekti koji
        // implementiraju sve funkcije koje meni trebaju!
        NSString *ext = [obj pathExtension];
        // .... 
      }

      Tu naravno slijedi napomena da ako računam da su unutra NSStringovi, ljepše je to i navesti u deklaraciji varijable obj u for petlji. Tada ću imati sve one lijepe provjere i sl. Zanimljivo je da će stvar raditi i ako su unutra spremljeni objekti koji nisu NSString — nema provjere.

      Sad bih se mogao vrtiti u krug. No, cijela priča oko ove dinamike je slična kao kod Pythona… samo što actually imam nekakvu compile-time provjeru, pa da barem warninge dobijem. Dakle, relaksiranost i dinamičnost Pythona uz neobavezne, ali uobičajene i poželjne compile-time provjere nekog klasičnog kompajliranog jezika.

      I naravno, možda najbitnije u kontekstu tvojeg komentara, a što nisam naveo u tekstu: ako funkcija ne postoji na instanci objekta, bacit će se exception koji, s obzirom da exceptione ne uobičavam handleati u Objective-C (i malo je nespretno bilo do Objective-C 2.0) znači i kršenje i kritičnu pogrešku. Slično kao kod Pythona. Što znači da čovjek bude oprezniji. Za razliku od Pythona, gdje je uobičajeno staviti try .. except blok i prešutiti grešku.

      3. Ne moraš castati. NSArray u sebi drži ideve, ili bolje rečeno NSObjecte. Usudio bih se reći da on čuva samo pointere (jer efektivno to i radi), no s obzirom da prilikom svakog poziva na -[NSArray addObject:] automatski se radi poziv [prviArgument retain];, to znači da bi se na pointeru koji nije Objective-C objekt program skršio. I to ružno, jer nema lijepog načina da se detektira da li je pokazivač Objective-C objekt.

      std::vector ne može držati bilo što — on može držati samo one objekte koji su mu definirani u predlošku. Što znači da moramo definirati baznu klasu i držati unutra pokazivače na derivirane objekte… što je, kao što vidiš, vrlo sličan princip. Nažalost, problem nastaje kada unutra moraš držati third-party objekte koji ne slušaju tvoje principe. Problem nastaje i u tome da se std::vector neće automatski brinuti za tvoj reference counting (retain/release cycle)… što znači da ako želiš da se bar za tvoje objekte brine, sad već moraš raditi novi container.

      A da ne pričamo o tome da u tom slučaju, cijela poanta templateiranja postaje apsurdna i bespotrebna — jer si iovako ionako htio napraviti generički spremnik, što znači da su svi objekti izvedeni iz jedne bazne klase, što znači… da. To znači da se u C++ pokušava imitirati Objective-C, a bez sve njegove dinamike i koristeći nespretni RTTI koji nosi svoje probleme.

      Ti pak kažeš kako je za održivi kod nužno specificirati tip podataka i držati ga se vrlo usko. U Objective-C nisam se susreo sa situacijom gdje nisam mogao nazvati varijablu, primjerice, NSArray* personArray; i u njoj čuvati instance klase Person. Tu se vidi da je očito što se unutra drži. Stoga održivost po mom mišljenju ne dolazi u pitanje.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 

What is 3 + 15 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)