Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] Porady do kodu i biblioteki, gra Snake

Ostatnio zmodyfikowano 2019-07-02 23:55
Autor Wiadomość
W1ll_3ty
Temat założony przez niniejszego użytkownika
[SFML] Porady do kodu i biblioteki, gra Snake
» 2019-07-02 10:04:12
Podam zaraz poniżej kod do Węża, którego sam stworzyłem przy użyciu biblioteki SFML 2.0 . Proszę o ocenę kodu i ewentualnie napisanie, co można zmienić. Kod nie jest w 100% ukończony, jeszcze brakuje kilku aspektów rozgrywki, ale tak to gra pięknie działa. Chciałem też się zapytać o operowanie czasem. Chciałem użyć obsługi czasu do wyświetlania przez 2 sekundy napisu "Lose!" ale nie zawsze (prawdopodobnie) program wychodził z pętli i wydaje mi się, że to z powodu że nie zawsze było idealnie 2 sekundy ale np. 2,01 bo jednak ten przebieg pętli chwilę trwa i przez to już nie chciało wyjść. Być może rozwiązaniem jest użycie warunku powiedzmy od 1,9 do 2,1 sekundy. Drugim moim pytaniem jest wielowątkowość czy jakoś tak. Jakoś dziwnie się czuję gdy powiedzmy pierw porusza się głowa węża a potem ogon, niby to nic takiego bo i tak tego nie widać ale jestem ciekaw jak to wygląda w... życiu :D
C/C++
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System/Clock.hpp>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>

using namespace sf;

// KLASY

class Ogonek {
public:
    Vector2f miejsce;
    Ogonek * nast;
    int stopa;
    Sprite ogon;
   
    void wypelnij( Vector2f poz, Texture & tekstura, int pop )
    {
        ogon.setTexture( tekstura );
        ogon.setOrigin( 6, 6 );
        stopa = pop;
        nast = 0;
       
        if( pop == 1 )
        {
            poz.y += 10;
            ogon.setPosition( poz );
        }
       
        if( pop == 2 )
        {
            poz.y -= 10;
            ogon.setPosition( poz );
        }
       
        if( pop == 3 )
        {
            poz.x += 10;
            ogon.setPosition( poz );
        }
       
        if( pop == 4 )
        {
            poz.x -= 10;
            ogon.setPosition( poz );
        }
       
    }
};

// KONIEC KLAS

int rX = 200;
int rY = 200;

void przegrac( Text & przeg, RenderWindow & okno, Sprite & pionek )
{
    pionek.setPosition( rX / 2 + 5, rY / 2 + 5 );
    Time krag;
    Clock zegar;
    okno.draw( przeg );
    okno.display();
    //do{
    // krag = zegar.getElapsedTime();
    //}while(krag.asSeconds() != 2 );
}

float X()
{
    while( 1 ) {
        int a =( rand() % rX - 6 ) + 6;
        if(( a % 10 ) == 5 )
             return a;
       
    }
}

float Y()
{
    while( 1 ) {
        int a =( rand() % rY - 6 ) + 6;
        if(( a % 10 ) == 5 )
             return a;
       
    }
}

Ogonek * znajdzOst( Ogonek * lista )
{
    while( lista->nast )
         lista = lista->nast;
   
    return lista;
}

void usunOgon( Ogonek * tail )
{
    if( tail->nast )
         usunOgon( tail->nast );
   
    tail->nast = 0;
    delete tail;
}

void przekazPozycje( Ogonek * tail, Vector2f xy, int ruch )
{
    if( tail->nast )
         przekazPozycje( tail->nast, tail->miejsce, tail->stopa );
   
    tail->ogon.setPosition( xy );
    tail->miejsce = xy;
    tail->stopa = ruch;
}

void rysowanie( Ogonek * tail, RenderWindow & okno )
{
    if( tail->nast )
         rysowanie( tail->nast, okno );
   
    okno.draw( tail->ogon );
}

int main()
{
    srand( time( 0 ) );
   
    int ruch = 0; // 0 brak, 1 gora, 2 dol, 3 lewo, 4 prawo
   
    RenderWindow oknoAplikacji( VideoMode( rX, rY, 32 ), "Snake", Style::None );
    oknoAplikacji.setFramerateLimit( 7 );
   
    // TEKSTURY
    Texture pion;
    Texture pkt;
    Texture ogo;
   
    if( !pion.loadFromFile( "box.png" ) ) {
        oknoAplikacji.close();
        return 50; }
    if( !pkt.loadFromFile( "point.png" ) ) {
        oknoAplikacji.close();
        return 50; }
    if( !ogo.loadFromFile( "ogo.png" ) ) {
        oknoAplikacji.close();
        return 50; }
   
    //SPRAJTY
    Sprite pionek;
    Sprite punkt;
   
    punkt.setTexture( pkt );
    punkt.setOrigin( 6, 6 );
    punkt.setPosition( X(), Y() );
   
    pionek.setTexture( pion );
    pionek.setOrigin( 6, 6 );
    pionek.setPosition( rX / 2 + 5, rY / 2 + 5 );
   
    String wynik[ 10 ] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
    int wyk = 0;
   
    Font czion;
    czion.loadFromFile( "arial.ttf" );
   
    Text text;
    text.setFont( czion );
    text.setString( wynik[ wyk ] );
    text.setPosition( rX / 2, 0 );
    text.setScale( 0.8, 0.8 );
   
    Text przegrana;
    przegrana.setFont( czion );
    przegrana.setString( "Lose!" );
    //przegrana.scale(4,4);
    przegrana.setPosition( rX / 3, rY / 2 - 10 );
    przegrana.setOrigin( 15, 5 );
   
    Ogonek * tail = 0;
   
    while( oknoAplikacji.isOpen() )
    {
        oknoAplikacji.clear( Color::Black );
        Event zdarzenie;
       
        while( oknoAplikacji.pollEvent( zdarzenie ) )
        {
            if( zdarzenie.type == Event::Closed ||( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Escape ) ) {
                oknoAplikacji.close();
                if( tail ) {
                    usunOgon( tail );
                    tail = 0;
                }
            }
           
            if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Up )
            {
                ruch = 1;
            }
           
            if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Down )
            {
               
                ruch = 2;
            }
           
            if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Left )
            {
                ruch = 3;
            }
           
            if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Right )
            {
                ruch = 4;
            }
        } // PETLA
       
        // PRZEGRANA
        Vector2f wek = pionek.getPosition();
        if( wek.y == - 5 || wek.y == rY + 5 || wek.x == - 5 || wek.x == rX + 5 )
        {
            przegrac( przegrana, oknoAplikacji, pionek );
            text.setString( '0' );
            do {
                punkt.setPosition( X(), Y() );
            } while( punkt.getPosition() == pionek.getPosition() );
           
            ruch = 0;
            if( tail ) {
                usunOgon( tail );
                tail = 0;
            }
            oknoAplikacji.clear( Color::Black );
        }
       
        // RUCH OGONA
        if( tail )
             przekazPozycje( tail, pionek.getPosition(), ruch );
       
        // KONTROLA RUCHU
        if( ruch == 1 ) {
            pionek.move( 0, - 10 );
            pionek.setRotation( 0 ); }
        if( ruch == 2 ) {
            pionek.move( 0, 10 );
            pionek.setRotation( 180 ); }
        if( ruch == 3 ) {
            pionek.move( - 10, 0 );
            pionek.setRotation( 270 ); }
        if( ruch == 4 ) {
            pionek.move( 10, 0 );
            pionek.setRotation( 90 ); }
       
       
       
        // ZDOBYWANIE PUNKTU
        if( pionek.getPosition() == punkt.getPosition() )
        {
            wyk++;
            if( wyk == 10 )
                 wyk = 0;
           
            text.setString( wynik[ wyk ] );
            punkt.setPosition( X(), Y() );
            if( !tail ) // DODAWANIE DO OGONA
            {
                tail = new Ogonek;
                tail->wypelnij( pionek.getPosition(), ogo, ruch );
            }
            else
            {
                Ogonek * ostatni = znajdzOst( tail );
                ostatni->nast = new Ogonek;
                Ogonek * nowy = ostatni->nast;
                nowy->wypelnij( ostatni->miejsce, ogo, ostatni->stopa );
            }
        }
       
        // RYSOWANIE
        oknoAplikacji.draw( pionek );
        if( tail )
             rysowanie( tail, oknoAplikacji );
       
        oknoAplikacji.draw( punkt );
        oknoAplikacji.draw( text );
        oknoAplikacji.display();
    }
    return 0;
}
 
P-174870
pekfos
» 2019-07-02 18:59:27
C/C++
class Ogonek {
public:
    Vector2f miejsce;
    Ogonek * nast;
    int stopa;
    Sprite ogon;
Nie po to są klasy, żeby wszystko było publiczne.

C/C++
if( pop == 1 )
{
    poz.y += 10;
    ogon.setPosition( poz );
}

if( pop == 2 )
{
    poz.y -= 10;
    ogon.setPosition( poz );
}

if( pop == 3 )
{
    poz.x += 10;
    ogon.setPosition( poz );
}

if( pop == 4 )
{
    poz.x -= 10;
    ogon.setPosition( poz );
}
Niepotrzebnie kopiowane ogon.setPosition(poz), switch na resztę.

C/C++
void przegrac( Text & przeg, RenderWindow & okno, Sprite & pionek )
{
    pionek.setPosition( rX / 2 + 5, rY / 2 + 5 );
    Time krag;
    Clock zegar;
    okno.draw( przeg );
    okno.display();
    //do{
    // krag = zegar.getElapsedTime();
    //}while(krag.asSeconds() != 2 );
}
Nazwę funkcji pisał tłumacz google. Czemu obliczenia w ustawianiu pozycji są skopiowane w wielu miejscach w programie? Zakomentowany kod nie ma żadnego sensu w tego typu programie.

C/C++
float X()
{
    while( 1 ) {
        int a =( rand() % rX - 6 ) + 6;
        if(( a % 10 ) == 5 )
             return a;
       
    }
}
Co to znaczy X? Jak cię interesują tylko liczby z ostatnią cyfrą równą pięć, to losuj x, zwracaj 10*x+5.

C/C++
float Y()
{
    while( 1 ) {
        int a =( rand() % rY - 6 ) + 6;
        if(( a % 10 ) == 5 )
             return a;
       
    }
}
Co to znaczy Y? I czym się różni od X?

C/C++
void usunOgon( Ogonek * tail )
{
    if( tail->nast )
         usunOgon( tail->nast );
   
C/C++
void przekazPozycje( Ogonek * tail, Vector2f xy, int ruch )
{
    if( tail->nast )
         przekazPozycje( tail->nast, tail->miejsce, tail->stopa );
C/C++
void rysowanie( Ogonek * tail, RenderWindow & okno )
{
    if( tail->nast )
         rysowanie( tail->nast, okno );
Po co ta rekurencja?

C/C++
oknoAplikacji.setFramerateLimit( 7 );
7 klatek na sekundę?

C/C++
if( !pion.loadFromFile( "box.png" ) ) {
    oknoAplikacji.close();
    return 50; }
if( !pkt.loadFromFile( "point.png" ) ) {
    oknoAplikacji.close();
    return 50; }
if( !ogo.loadFromFile( "ogo.png" ) ) {
    oknoAplikacji.close();
    return 50; }
Więcej kopiowania kodu, wystarczyłoby || i jeden if. Albo sam return, okno i tak się zamknie.

C/C++
String wynik[ 10 ] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
Wbrew pozorom '0'+x to mniejszy hack. Albo wyświetlaj liczbę jak należy, Wstawianie zmiennych do tekstu.

C/C++
if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Up )
{
    ruch = 1;
}

if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Down )
{
   
    ruch = 2;
}

if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Left )
{
    ruch = 3;
}

if( zdarzenie.type == Event::KeyPressed && zdarzenie.key.code == Keyboard::Right )
{
    ruch = 4;
}
Więcej kopiowania kodu. Lepiej if w if, albo switch w if.

C/C++
wyk++;
if( wyk == 10 )
     wyk = 0;

Czyli 9 punktów to wyższy wynik niż 10?

P-174873
W1ll_3ty
Temat założony przez niniejszego użytkownika
» 2019-07-02 21:57:08
Faktycznie, klasę zamienię na strukturę, już o tym myślałem ale obawiałem się, że struktury mogą bardzo się różnić i chciałem też poćwiczyć na klasach. Czyli klasa od struktury różni się tylko tym, że w klasie trzeba napisać public by coś było publiczne, a w strukturze nie? I w strukturach też można używać private: i protect: ?
Oo tak zapomniałem o switch a to będzie o wiele ładniej wyglądać.
Ustawianie pozycji tak wygląda bo to rX i rY które jest miedzy innymi w funkjach X() i Y() to są rozdzielczości okna, gdybym kiedyś np dodał ekran samodzielnego wygrania szerokości i wysokości, poza tym miałem problem z ustawianiem na samym środku, także to muszę jeszcze zmienić tak samo funkcje X() i Y() które mega mi się nie podobają ale się na razie nimi nie zajmowałem.
Rekurencja jest po to żeby przekazywać tak jakby po kolei dane że powiedzmy przedostatni kwadrat z ogona przekazuje ostatniemu, skoro nie ma następnego to ten ostatni przyjmuje te pozycje która dostał, wtedy wracamy do poprzedniego itd. Wydawało mi się że to najlepszy sposób.
Musiałem ustawić limit klatek bo inaczej jak się tylko kliknie przycisk np. w prawo to on przemieszcza się "od razu" w prawo. Jak zrobić to inaczej?
Tak, zapomniałem o tym że tabela jest wskaźnikiem na jej pierwszy element, a co do zmiany punktów na zero gdy osiąga 10: chciałem po prostu by ten system punktów był i później go rozwinąć, bo tak to musiałbym już wtedy powiedzmy 30 razy inicjalizować stringi.
Dzięki wielkie za wszystkie rady pekfos! :D
PS A co z tą obsługą czasu?
P-174875
pekfos
» 2019-07-02 23:30:53
C/C++
do {
    krag = zegar.getElapsedTime();
} while( krag.asSeconds() != 2 );
To nie ma sensu, bo czas jaki to porównujesz jest zmiennoprzecinkowy, więc praktycznie nigdy nie będziesz mieć dokładnie dwóch sekund. Nie piszesz na FPGA, nie używaj najsłabszego warunku jaki się da. Powinno być krag.asSeconds() < 2. Ale to i tak nie jest największy problem - ta pętla nie ma sensu, bo w czasie jej trwania nie odświeżasz ekranu, ani nie obsługujesz zdarzeń. W tego typu aplikacji główna pętla programu musi się wykonywać nieprzerwanie.
Zrób sobie główny zegar clock i zmienną dt, do której będziesz w głównej pętli przypisywać clock.restart(), jako sf::Time, albo float z liczbą sekund. dt zawiera w gruncie rzeczy czas trwania jednego cyklu. Jeśli coś ma się zdarzyć i trwać 2 sekundy, to z tym powinna być związana gdzieś zmienna mówiąca, jak długo jeszcze zdarzenie ma trwać i aktualizując stan gry pomniejszasz ten czas o dt. Wiedząc jaki był czas startowy bez problemu możesz wyznaczyć jaki procent czasu upłynął i na podstawie tego wyświetlać jakąś animację, np modyfikując przezroczystość, czy pozycję napisu 'przegrałeś'.
P-174878
W1ll_3ty
Temat założony przez niniejszego użytkownika
» 2019-07-02 23:55:21
Racja, dzięki :)
P-174880
« 1 »
  Strona 1 z 1