Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Peliohjelmointia...

Sivun loppuun

Cc [08.01.2006 17:36:13]

#

Kumpi kieli soveltuisi teidän mielestä paremmin peli ohjelmointiin c vai c++? mitä etuja ja haittoja kielissä on ohjelmoidessa pelejä? onko aiheesta juttua jossakin muualla, itse yritin etsiä mutta en paljoa löytännyt. ja onko jossakin hyviä oppaita pelien ohjelmointiin kyseisellä kielellä?

Tumpelo [08.01.2006 17:40:07]

#

Kumpikin on aikalailla yhtä hyviä, ainut selkeä ero on se että C++:lla voi käyttää olioita... Mutta minusta henkilökohtaisesti C++ on selkeämpi ja muutenkin mukavempi käyttää kuin C. Mutta lopputuloksen voi kummallakin saada yhtä hyvän.

Suosittelen kuitenkin C++:aa.

Edit: Miksi käyttää "vanhentunutta" kieltä kun sen tilalle on viitsitty kehitellä uusi versio...

Antti Laaksonen [08.01.2006 17:51:55]

#

Tärkein asia on, että

Tumpelo kirjoitti:

lopputuloksen voi kummallakin saada yhtä hyvän.

Minä varmaan käyttäisin C:tä, kun osaan sitä paremmin.

phadej [08.01.2006 18:08:44]

#

murinaa. C++ ei ole C:n uusi versio. C++ ei edes ole C:n superset (enää).

os [08.01.2006 18:43:27]

#

C:llä saa tehtyä nopeampia ja pienempiä ohjelmia kuin C++:lla. C++:ssa on toisaalta joitakin monimutkaisten pelien kannalta korvaamattomia ominaisuuksia, kuten mallit. Yksinkertainen peli kannattaa varmaan tehdä C:llä, jollei kaipaa C++:n kätevyyksiä, kuten:

for(int i=1, b=1; i<max; i++) b *= i;

hunajavohveli [08.01.2006 18:56:46]

#

Riippuu siitä, kuinka paljon on käyttöä C++:n lisäominaisuuksille. Jos on esim. vasta aloittelija, niin silloin on parempi käyttää C:tä ja ottaa C++:n ominaisuuksia käyttöön sitä mukaa, kun tarvitsee.

Metabolix [08.01.2006 18:58:56]

#

Tuosta nimenomaisesta C++:n "kätevyydestä" on ainakin minulle ollut enemmän harmia kuin hyötyä. Sman tien ne muuttujat määrittelee kerralla alussa.

ikkah [08.01.2006 20:16:40]

#

Käytä sitä mitä osaat, tai jos osaat kumpaakin niin käytä C++:aa :)

C++:ssa on mukavia ominaisuuksia jotka auttavat pienissäkin projekteissa, esim. standardikirjaston stringit ja vectorit. C++ on aloittelijallekin mukavampi kun ei tarvitse heti opetella pointtereiden ja taulukkojen kanssa pelaamista. Oliot, nimiavaruudet ja muut auttavat sitten kun peliprojekti kasvaa. C++:lla voi myös tehdä aivan yhtä nopeita ohjelmia kuin C:lläkin, ja kokokin on sama jos standardikirjastoa ei lasketa.

Minusta tuo selkeyttää että voi määrittää muuttujat vasta siinä vaiheessa kun niitä käytetään. Eihän sitä tietenkään pakko ole käyttää, ja eikös C99:ssä ole tuo ominaisuus myös?

os [08.01.2006 20:17:05]

#

Olennaistahan ei tietenkään ole se, mitä ominaisuuksia käyttää ja mitä ei, vaan se, saako ohjelman kääntyymään C:nä.

edit: Muutenhan kannattaa tietenkin käyttää C++:ssaa :), vrt:

int *cpp = new int[10];
int *c = (int*)malloc(10*sizeof(int));

Cc [08.01.2006 20:42:15]

#

Onko jossain päin sitten hyvää materiaalia c++:salla pelien tekemiseen? kuinka paljon win c++ ja linuxin c++ eroaa toisistaan? Mitä "hyviä" ominaisuuksia sitten siinä c++:sassa on?

TeeVee [08.01.2006 21:27:24]

#

http://www.gamedev.net


Käytä järjestelmäriippumattomia rajapintoja, jotta saat portattua pelisi monelle alustalle. Windows ja linux omaavat saman standardin c++:n (windowsissa ei oletuksena). Jotkin lisäkirjastot eivät aina toimi molemmissa (tarkoititko tätä?).

Metabolix [09.01.2006 01:31:28]

#

os kirjoitti:

edit: Muutenhan kannattaa tietenkin käyttää C++:ssaa :), vrt:

int *cpp = new int[10];
int *c = (int*)malloc(10*sizeof(int));

Ai jaa?
vrt.

// C++-resize
int *cpp = new int[vanha_koko];
int *tmp = new int[uusi_koko];
memcpy(tmp, cpp, sizeof(int) * (vanha_koko < uusi_koko ? vanha_koko : uusi_koko));
delete [] cpp;
cpp = tmp;

// C-resize
int *c = (int *) malloc(10 * sizeof(int));
c = (int *) realloc(c, uusi_koko);

No joo. C++:ssa ei pitäisi joutua muuttamaan kokoa; jos sitä tarvitsee, niin pitäisi käyttää std::vector-luokkaa. Mutta periaatteessa noin rumasti.

koo [10.01.2006 20:51:42]

#

os kirjoitti:

C:llä saa tehtyä nopeampia ja pienempiä ohjelmia kuin C++:lla.

Jos otan C-koodin ja käännän sen C++-kääntäjällä, niin tuloksena ei taida olla yhtään sen hitaampi ja suurempi ohjelma kuin C-kääntäjälläkään.

C++:n I/O-kirjasto tuppaa kyllä usein olemaan C:n vastinetta hitaampi. Toisaalta esimerkiksi C++:n sort toimii usein nopeammin kuin C:n qsort, vaikka samaa algoritmia käyttävätkin.

Kyllä minä vaan käyttäisin C++:aa, on se niin paljon kätevämpi.

Heikki [10.01.2006 22:09:27]

#

koo kirjoitti:

Kyllä minä vaan käyttäisin C++:aa, on se niin paljon kätevämpi.

Samaa mieltä. Ohjelmointi puhtaalla C:llä tuntuu jotenkin riisutulta ja rajoittuneelta (siis tuntuu, en ole mitään ihmeempä puhtaalla C:llä tehnyt). Olit, vektorit, stringit jne. ja elämä on paljon helpompaa :)

Tumpelo [11.01.2006 17:51:26]

#

Tutustuin tässä olio-ohjelmointiin ja kyllä se on erittäin hyödyllinen ominaisuus. Koodi on selkeämpää, järeämpää, luotettavampaa ja uudistettavampaa. Tästä huvista C:n käyttäjät jäävät paitsi. :P

Cc [12.01.2006 00:30:45]

#

Tutustuin itsekkin olio-ohjelmointiin ja c++:saan peliohjelmoinnissa ja se on kylläkin todella paljon selkeämpää minusta kuin normaali c:n käyttö, kiitos vastanneille.

koo [12.01.2006 22:48:53]

#

Cc kirjoitti:

Tutustuin itsekkin olio-ohjelmointiin ja c++:saan...

:-) Syvällisempään perehtymiseen saattaakin sitten mennä vuosia... Mutta on se parempi harrastus kuin mummojen potkiminen, joten onnea vaan matkaan. Tai mistäs minä tiedän, kun en ole käynyt potkimassa. :-P

Metabolix kirjoitti:

No joo. C++:ssa ei pitäisi joutua muuttamaan kokoa; jos sitä tarvitsee, niin pitäisi käyttää std::vector-luokkaa. Mutta periaatteessa noin rumasti.

Nämä esimerkit ovat vähän huonoja, kun taulukkovarauksia ei kunnon C++-koodissa pahemmin tehdä. Kyllä C++:ssa saa käyttää kirjastoluokkia yhtä lailla kuin C:ssä kirjastofunktioita. Rumuus on tietenkin katsojan silmässä, mutta vertaillaanpa nyt vielä niin, että asiat tehdään oikein:

// C++-resize
std::vector<int> cpp(vanha_koko);
cpp.resize(uusi_koko);

// C-resize
int *c = malloc(vanha_koko * sizeof(int));
if (c == NULL) abort();
int *tmp = realloc(c, uusi_koko * sizeof(int));
if (tmp == NULL) abort();
c = tmp;
free(c);

Kommenttina vielä, että C:ssä muistinvarausfunktioiden paluuarvojen castaus on turhaa ja toteutuksesta riippuen jopa vaarallista. C++:ssa nimenomaan malloc:in ja kumppaneiden paluuarvojen castaus on pakollista, mikä onkin pikku vihje olla käyttämättä noita C-funktioita C++-koodissa.

phadej [12.01.2006 23:57:14]

#

Tossa C++ koodissa taas ei ole virhetarkistusta
ja reallocia varten ei tarvi väliaikaista muuttujaa. Eikä oikein saisi muuttujia esitellä siellä täällä.

C++ tapa on kieltämättä vähän siistimpi, varsinkin jos lohkossa on monta samaa poikkeusta heittävää operaattoria, koska siihen riittää vain yksi catch. Toisaalta mikään ei estä tehdä 'xmallocia' ja 'xreallocia' (glibc:n manualissa taitaa olla peräti esimerkki), jotka hoitavat tarkistuksen. C99:ssä on varsinkin inline funktiot, joten ei tulisi edes overheadia siitä.

C:ssä silti kannattaa tehdä kastaus, ja se on hyvä tapa, koska kääntääjän saa herjäämään vääräntyyppisistä sijotteluista.

Turhaa näiden kielien eroista kiistää. Toinen tykkää toisesta ja toinen toisesta.

// C++
try {
    std::vector<int> cpp(vanha_koko);
    cpp.resize(uusi_koko);
}
catch (bad_alloc) (
    abort();
}

cpp.clear();

/* C */
int *c = (int) malloc(vanha_koko * sizeof(int));
if (!c) abort();

c = (int) realloc(c, uusikoko * sizeof(int));
if (!c) abort();

free(c);

edit: oletan että vector heittäisi bad_allocia, vielä kun löytyis jostain dokua

koo [13.01.2006 01:14:05]

#

phadej kirjoitti:

Tossa C++ koodissa taas ei ole virhetarkistusta
ja reallocia varten ei tarvi väliaikaista muuttujaa. Eikä oikein saisi muuttujia esitellä siellä täällä.

Kyllä siinä on tuota C:tä vastaava tarkastus: jos vector heittää bad_alloc:in tai muun poikkeuksen, ohjelma kuolee hallitusti. catch ja abort ovat tarpeettomia.

Väliaikainen muuttuja tarvitaan, jos realloc:in epäonnistumisen jälkeen aiotaan tehdä yhtään mitään muuta, kuin abort tms., muuten alkuperäinen osoitin menetetään. Mutta totta, ei se tässä ihan pakollinen olisi.

Viimeisin C-standardi taitaa antaa esitellä muuttujia muuallakin kuin lohkojen alussa. On ihan järkevää esitellä muuttujat mahdollisimman lähellä sitä paikkaa, jossa niitä käytetään.

phadej kirjoitti:

C:ssä silti kannattaa tehdä kastaus, ja se on hyvä tapa, koska kääntääjän saa herjäämään vääräntyyppisistä sijotteluista.

Joo, jos itse castaa väärään tyyppiin. Muissa, tavallisemmissa virheissä se nimenomaan tukkii kääntäjän turvan, sillä vaikka kääntäjällä olisikin funktioesittelyjen yms. puolesta jotakin valittamista, casti kertoo kääntäjälle, että ok, koodaaja näyttää tietävän paremmin. Siis: huono tapa.

Vaaralliseksi castaus muuttuu vaikkapa tilanteessa, jossa kääntäjä ikänsä tai jonkun yhteensopivuusmoodin takia sallii esittelemättömien funktioiden kutsumisen. Leikitään vielä, että int on 32-bittinen ja osoittimet ovat 64-bittisiä. Mitä tapahtuukaan tässä, jos headeria <stdlib.h> ei ole includoitu?

int *pi = (int *) malloc(sizeof(int));

Funktiota ei ole esitelty, joten sen paluuarvo oletetaan int:ksi. Casti muuttaa sitten tuon 32-bittisen arvon 64-bittiseksi osoittimeksi. Funktio oli kuitenkin alun alkaen tarjoamassa 64-bittistä void-pointteria, joka sellaisenaan castautuu C:ssä tarvittaessa int-pointteriksi. Nyt vaan puolet osoittimen sisällöstä voi korvautua hötöllä!

std::vector-muuttujaa ei tarvitse clearata käytön jälkeen. Sen sisältö tuhotaan samalla kun muuttuja itsessään lakkaa olemasta esimerkiksi lohkosta poistumisen takia.

vector ei oikeastaan itse nakkele exceptioneita, mutta esimerkiksi std::bad_alloc voi tulla, kun vector allokoi tilaa alkioilleen. Muitakin poikkeuksia voi tulla, vaikkapa kun alkio on jokin olio, jonka kopioiva konstruktori päättää sellaisia viskoa.

phadej [13.01.2006 02:13:47]

#

Kuolee hallitusti, mutta ei kerro miksi teki niin.

Ja noita yhteensopivuusmoodia ei oikein käytetä. GCC:ssä on hyvä käyttää -Wall ja -Wstrict-prototypes vipuja niin ei tule yllätyksiä.

Ja kastaamisia on erillaisia,

// imo hyvä
int *ptr = (int*) malloc(sizeof(int) * 10);

// en tiedä miten tämän tekisi siististi ilman kastaamista
int i = 123;
int j = 10;
double d = (double)i / j;

// huono (esimerkkikin), mutta miksi kukaan tekisi niin
int n(int m) {
    return -m;
}

long l[10];
n ((int)l);

Tämä menee vähän jossitteluksi:
Jos reallocin epäonnistumiseen jälkein ajotaan tehdä yhtään mitään niin tarvisi väliaikainen muuttujaa, mutta sitten tarvisi C++:n try-catch:kin. Toisaalta se bad_allocin kiiniottajakaan tiedä containerista mitään, ellei se on esitetty jo ennen.

Niin kuin itse sanoit muitakin poikkeuksia voi tulla. ja niihin pitää varautua. vector on kiva, oikein käytettynä, mutta jos siihen tunkee jonkin oman classin joka on toteutettu huonosti (esim ei oo varautunut exceptioneihin) niin muistia voi vuotaa tai muuten sekoittaa containerin.

Laitoin ton clearin ihan sen takia, että jos siinä vaiheessa muistia on vapautettava.

Vektoria ja mallocia toisaalta on myös 'väärin' vertaila,
vectori on paljon korkeakielisempi rakenne (se käyttää itse allocia tai jopa mallocia jos jostain syystä sitä halua).

voihan olla noikin:

// Vector on siis typedef pointeriin jonkinnäköiseen structiin.

Vector v;
v = vector_new(vanha_koko);
vector_resize(v, uusi_koko);
vector_delete(v);

/* funkkarit voivat sitten epäonnistuessaan jo kutsua jonkinnäköisen errorhandlerin, joka voi sitten yrittää korjata tilannetta tai yksinkertaisesti lopettaa ohjelman. */

C:ssä voikin siis tehdä jonkinlaisen vektorityypisen rakenteen, jos sellaista haluaa. C++ tarjoa sen jo vakiona ja aika hyvin toteutettuna.

ainiin, kauneus on katsojan silmissä.

selvitykseksi: en itse osaa sanoa kummasta kielestä pidän enemmän.
C++:ssa ottaa päähän g++ (hitaus) eniten, toivottovasti siihen tulee parannusta. Ja sitten alkuun se oli aika valtava kieli. C:stäkään osaa kaikia juttuja, niinkuin todettu, oppimiseen menee vuosia.

edit: ainiin piti olla std::bad_alloc aikaisemmassa postissa. namespacet on toisaalta kivoja toisaalta rasittavia. kun ei viiti usingiakaan turhaan käyttää.

koo [13.01.2006 10:23:34]

#

Ei se C:n abortkaan pahemmin kerro, että mikä tuli.

Yhteensopivuusmoodeja ei kannattaisi käyttää, mutta maailmassa on ihmeen paljon esimerkiksi K&R-tyyppistäkin C-koodia eikä esittelemättömien funktioiden kutsuminen ole ollut laitonta ennen viimeisintä C-standardia.

Castaamista pitää käyttää vain kun se on ehdottomasti tarpeen, ei mitenkään rutiininomaisesti. Kielen omat säännöt ovat turvallisempia, kääntäjän varoitukset on syytä ottaa tosissaan eikä niitä kannata vaientaa omilla casteilla.

// tarpeeton ja jopa vaarallinen cast
int *ptr = (int*) malloc(sizeof(int) * 10);

// castiton vaihtoehto
int i = 123;
int j = 10;
double d = i;
d /= j;

// Mahtava juttu, annetaan parametrina pointteri longiin,
// kun funktio odottaa intiä! Castin takia kääntäjä päästää
// koodin läpi, koska koodaaja selvästikin haluaa niin.
int n(int m) {
    return -m;
}

long l[10];
n ((int)l);

phadej kirjoitti:

Jos reallocin epäonnistumiseen jälkein ajotaan tehdä yhtään mitään niin tarvisi väliaikainen muuttujaa, mutta sitten tarvisi C++:n try-catch:kin. Toisaalta se bad_allocin kiiniottajakaan tiedä containerista mitään, ellei se on esitetty jo ennen.

Tuo vector on ihan samassa tilassa ja sen sisältö on sama kuin ennen exceptionia. try-catch:llä voidaan kyllä jatkaa, mutta jos kiinniottaja on jossain muualla, sen ei tarvitsekaan tietää containerista mitään, sillä siihen mennessä pino on jo siivottu.

Omatekemien luokkien pitää olla aina exception safe. Tämä on C++-koodarin perusohjesääntö, jota noudattamalla elämä muuttuu paljon huolettomammaksi, varsinkin jos vielä käyttää tapaa, että varailee muistit ja muut resurssit konstruktoreissa ja vapauttaa ne viimeistään destruktoreissa (ns. RAII-idiomi).

Tämä ei toteudu tuossa Vector-esimerkissä, jossa vector_delete(v) voi vahingossa unohtua tekemättä.

C++:n kääntäminen on hitaampaa, koska kääntäjä käsittelee suuremman määrän tietoa kuin C-kääntäjä. C++ on myös C:tä laajempi kieli. Lopputuloksena syntyvä ajettava ohjelma ei kuitenkaan ole luonnostaan yhtään hitaampi kuin C:n tapauksessa, mahdolliset erot syntyvät muista syistä.

phadej [13.01.2006 14:44:12]

#

offtopic: ompa sparcini gcc ja g++ "sekaisin", g++ käänettynä on 20% nopeampi kuin gcc:llä. c++ versio (eli vähän hienompaa koodia, vectoria käyttää jne) taas on noiden kahden välissä.

Vertailuksi, x86:lla gcc:lla ja g++ käänettyt versiot ovat yhtä nopeita ja tiedostokokokin on g++ vaan vähän isompi (tunkee mukaan alle kilotavun sitä c++ runtimia, -fno-rtti -fno-exceptions tekee täysin samankokoiseksi, kun ei niitä C ohjelmassa kummiksaan tarvi).

Ohjelmana oli sellainen erikoinen breadth-first search (dt06 toinen tehtävä), alkoritmi c ja c++ versioissa sama, toteutus tokin eri. (Minun) c++ toteutus on sitten kai vaan huonompi.

c++ kääntäjän hitaus lienee vaan STL, sitä kun käänetään aina vähän erilaisena, templateja siis.

Ei kai voi mitään, on se c++ vähän hianompi kiäli.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta