Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C ja SDL, perusmuodon liikuttuminen

Sivun loppuun

Ville [30.10.2009 16:47:05]

#

Lueskelin täällä putkassa SDL oppaan ja vielä aika monta lisää netin syövereistä :)

Koetikin sitten tehdä pikku kokeilua asian tiimoilta.
Ajatus oli piirtää ruudulle neliö jota sitten voisi nuolinäppäimillä liikutella.
Ohjelma toimii muuten ok, paitsi että kun näppäintä painaa liikahtaa kuvio kerran, eli näppäintä joutuu "rämpyttämään" jos haluaa kuvion liikkeelle.

Vaikka koetin apinoida putkan esimerkkiä jossa liikutettiin kuvaa aivan samoin while silmukan sisällä ja siinä kuva kyllä liikkui mukavan tasaisesti kaikkiin suuntiin.

Eli, mistäs nyt kiikastaa? Tässä viritelmäni :

#include <SDL/SDL.h>  // sisällytetään SDL:n otsikkotiedostot
#include <SDL/SDL_gfxPrimitives.h>

int main(int argc, char **argv)
{
   int ohjelmaKaynnissa, x1, y1, x2, y2 ;
   SDL_Init( SDL_INIT_VIDEO );
   SDL_Surface* naytto = SDL_SetVideoMode(800,600, 0,SDL_HWSURFACE | SDL_DOUBLEBUF );
   SDL_WM_SetCaption( "Kuvio kokeilu", 0 );

   Uint8* nappi;
   SDL_Event tapahtuma;
   ohjelmaKaynnissa = 1;

   x1 = 210;
   y1 = 80;
   x2 = 250;
   y2 = 120;
   while(ohjelmaKaynnissa)
   {
      if (SDL_PollEvent(&tapahtuma))
      {
         if (tapahtuma.type == SDL_QUIT)
         {
            ohjelmaKaynnissa = 0;
         }
         if( tapahtuma.type == SDL_KEYDOWN )
		 {
                    if( tapahtuma.key.keysym.sym == SDLK_ESCAPE ) /* näinkin voi lukea näppäimiä */
					{
					  	ohjelmaKaynnissa = 0;
  			        }
         }
        nappi = SDL_GetKeyState(NULL);
        if( nappi[SDLK_UP] )
		{
			  y1 -= 1;
		  	  y2 -= 1;
		}
        if( nappi[SDLK_DOWN] )
		{
		  	y1 += 1;
  	        y2 += 1;
		}
        SDL_FillRect(naytto, NULL, SDL_MapRGB(naytto->format, 0, 0, 0));
        boxRGBA(naytto, x1, y1, x2, y2, 255, 0, 0, 150);
      }
      SDL_Flip(naytto);
   }
   SDL_Quit();
   return 0;
}

Legu [30.10.2009 16:53:04]

#

Tavara tuosta nappi = SDL_GetKeyState(NULL); alkaen pitää olla tuon if-lauseen ulkopuolella (if (SDL_PollEvent(...))), koska muuten lootaa liikutetaan vain, kun nappula on pohjassa ja tulee eventti (eli vaikka näppäimen painallus tai hiiren liikutus), mikä siis on ongelmana.

Lisäksi kuviota kannattaa liikuttaa ajasta riippuvan määrän, jolloin liike on periaatteessa yhtä nopea silmukankiertonopeudesta (= koneen tehoista) riippumatta. Tuollaisessa kokeilussa se ei toki haittaa, mutta peleissä tämä menettelytapa on käytännössä välttämätön.

Ville [30.10.2009 17:33:34]

#

Legu kirjoitti:

Lisäksi kuviota kannattaa liikuttaa ajasta riippuvan määrän, jolloin liike on periaatteessa yhtä nopea silmukankiertonopeudesta (= koneen tehoista) riippumatta. Tuollaisessa kokeilussa se ei toki haittaa, mutta peleissä tämä menettelytapa on käytännössä välttämätön.

No johan lähti liikkeelle!
Melkoisella vauhdilla tosin...
Mitä tarkoitat tuolla liikuttamisella ajasta riippuvalla määrällä?

Legu [30.10.2009 17:47:54]

#

Sitä, että jokaisen sekunnin aikana lootaa liikutetaan aina sama määrä pikseleitä.

Nyt lootaa siirretään yksi pikseli joka kierroksella. Esim. nyt jos FPS = 100, eli kuvien välillä 1 / 100 sekuntia (= 10 ms), lootan nopeus olisi siis v = s/t = 1 pikseli / (1 / 100 sekuntia) = 100 pikseliä/sekunti. Tämä ei siis ole vakio, vaan riippuu juuri tuosta päivitysnopeudesta. Jos FPS olisi puolet alhaisempi, liikkuisi loota puolet hitaammin myös.

SDL_GetTicks-funktiolla saadaan aika millisekunteina ohjelman käynnistymisestä (tai oikeastaan SDL-kirjason alustuksesta) lähtien. Näin saadaan ohjelma "synkronoitua" oikeaan aikaan.

Uint32 alku = SDL_GetTicks();
while (päällä) {
    Uint32 nyt = SDL_GetTicks();
    alku = nyt;
    int ajan_muutos = nyt - alku;

    // ... koodia

    if (painetaan_nappulaa) {
        ukkelin_paikka += nopeus * ajan_muutos
    }

    // ... taas koodia
}

Ville [30.10.2009 18:57:42]

#

Legu kirjoitti:

Sitä, että jokaisen sekunnin aikana lootaa liikutetaan aina sama määrä pikseleitä.

OK.

lainaus:

Nyt lootaa siirretään yksi pikseli joka kierroksella. Esim. nyt jos FPS = 100, eli kuvien välillä 1 / 100 sekuntia (= 10 ms), lootan nopeus olisi siis v = s/t = 1 pikseli / (1 / 100 sekuntia) = 100 pikseliä/sekunti. Tämä ei siis ole vakio, vaan riippuu juuri tuosta päivitysnopeudesta. Jos FPS olisi puolet alhaisempi, liikkuisi loota puolet hitaammin myös.

En noista FPS jutuista vielä tajua mitään mutta toivon mukaan matkan varrella kirkastuu...

lainaus:

SDL_GetTicks-funktiolla saadaan aika millisekunteina ohjelman käynnistymisestä (tai oikeastaan SDL-kirjason alustuksesta) lähtien. Näin saadaan ohjelma "synkronoitua" oikeaan aikaan.

Onko käytettävä SDL funktioita vai toimiiko C:n omat funktiot tuossa hommassa?
Tarkoitan clock () funktiota ja CLOCKS_PER_SEC makroa.

Metabolix [30.10.2009 19:33:23]

#

C:n clock() palauttaa tiedon, paljonko prosessoriaikaa ohjelma on käyttänyt. Jos siis ohjelma pyörii täydellä teholla, se antaa suunnilleen oikean ajan, mutta jos ohjelma ei vaadi kaikkea koneen tehoa (ja parempi niin!), tulos on liian alhainen. Joka tapauksessa funktio on väärä tähän tilanteeseen. Muuten time(0) sopisi, mutta se valitettavasti laskee vain kokonaisia sekunteja. Kannattaa siis käyttää SDL:n funktiota, kun kerran SDL:ää käytät muutenkin.

Ville [30.10.2009 20:00:27]

#

Kiitos Metabolix. Koetan siis SDL:n kanssa.

Mutta valitettavasti on sen verran uusi asia tämä SDL etten Legun antamasti vinkistä nyt kiinni saanut :(
Näin sitä yritin soveltaa täällä esittämääni koodiin. Mutta ilmeisen huonolla menestyksellä koska liike loppui kokonaan :(

Tällä ei varmaan väliä onko tässä vai heti SDL_Init() jälkeen.
Tässä siis otetaan pohjaaika.*/

  alku = SDL_GetTicks();
   while(ohjelmaKaynnissa)
   {
      if (SDL_PollEvent(&tapahtuma))
      {
         if (tapahtuma.type == SDL_QUIT)
         {
            ohjelmaKaynnissa = 0;//false
         }
         if( tapahtuma.type == SDL_KEYDOWN )
	 {
            if( tapahtuma.key.keysym.sym == SDLK_ESCAPE )
	    {
		ohjelmaKaynnissa = 0;
  	    }
         }

      }

Täällä otetaan uusi verrattava aika

nyt = SDL_GetTicks();

Mutta tätä en ymmärrä.
Nythän molemmissa muuttujissa on sama arvo?

alku = nyt;

Edelliseen viitaten, eikös tästä laskutoimituksesta tule nolla?
Eli 1 * ajan_muutos = 0, jolloin liikettä ei aikaan saada.

ajan_muutos = nyt - alku;
  // liikutetaan laatikkoa
nappi = SDL_GetKeyState(NULL);
if( nappi[SDLK_UP] )
{
      y1 -= 1 * ajan_muutos ;
        y2 -= 1 * ajan_muutos ;

Metabolix [30.10.2009 20:05:24]

#

Legun koodissa onkin virhe. Ihmettelemäsi sijoitusrivi pitäisi olla vasta muutoksen laskemisen jälkeen, jotta arvo nyt olisi seuraavalla kierroksella arvona alku. Muutos saadaan siis tietenkin edellisen kierroksen ja tämän kierroksen aikojen erosta.

Legu [30.10.2009 20:22:46]

#

Metabolix kirjoitti:

Legun koodissa onkin virhe. Ihmettelemäsi sijoitusrivi pitäisi olla vasta muutoksen laskemisen jälkeen, jotta arvo nyt olisi seuraavalla kierroksella arvona alku. Muutos saadaan siis tietenkin edellisen kierroksen ja tämän kierroksen aikojen erosta.

Joo näköjään tollanen oli päässyt lipsahtamaan...

Ville [30.10.2009 20:58:38]

#

Okei, alkoi taas liike pelittämään.
Mutta molemmissa tapauksissa liike on yhtä nopeaa(ainakin silmämääräisesti). Oliko esimerkkini liian "alkeellinen" jolloin tuon ajan laskeminen ei pääse oikeuksiinsa vai tuleeko ero jossain muussa yhteydessä esille?

if( nappi[SDLK_UP] )
       {
             y1 -= 1 * ajan_muutos ;
               y2 -= 1 * ajan_muutos ;
       }
       if( nappi[SDLK_DOWN] )
       {
             y1 += 1;
             y2 += 1;
       }

No jatketaan tähän vielä.
Tuli vain mieleen että miksi putkan esimerkissä jossa näppäinpainalluksilla liikutetaan sinistä ruutua, ruutu "rullaa" mukavan hitaasti.
Kun taas tässä omassa tapauksessa kuvio menee melkoista kyytiä?

Metabolix [30.10.2009 21:25:08]

#

Isoissa peleissä ero on suuri. Voit testata tätä laittamalla silmukan loppuun vaikka SDL_Delay(50), jotta peli toimisi hitaammin. Eron varmasti huomaa.

Jos muutat koordinaatit float-tyyppisiksi, voit kasvattaa niitä alle yhden pikselin kerrallaan, jolloin liike on hitaampaa. (Piirtovaiheessa ne pitää pyöristää kuitenkin kokonaisluvuiksi.)

float nopeus_y = 0.1;
float y;
y += nopeus_y * kulunut_aika;

Ville [30.10.2009 21:36:45]

#

Metabolix kirjoitti:

Isoissa peleissä ero on suuri. Voit testata tätä laittamalla silmukan loppuun vaikka SDL_Delay(50), jotta peli toimisi hitaammin. Eron varmasti huomaa.

No itseasiassa tuota koetinkin, mutta meno muuttui melko nykiväksi :(

lainaus:

Jos muutat koordinaatit float-tyyppisiksi, voit kasvattaa niitä alle yhden pikselin kerrallaan, jolloin liike on hitaampaa. (Piirtovaiheessa ne pitää pyöristää kuitenkin kokonaisluvuiksi.)

float nopeus_y = 0.1;
float y;
y += nopeus_y * kulunut_aika;

Tuotakin koetin, muuttamalla vakio 1:sen 0.5:teen, mutta en x:ää ja y:tä floatiksi älynnyt muuttaa.
Täytyypä koettaa uudelleen :)

No niin tulipa kokeiltua.
Nyt toimii nätisti. Kiitos vinkistä!


Sivun alkuun

Vastaus

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

Tietoa sivustosta