SDL2 Jump'n Run Demo



  • Hallo zusammen,
    ich hoffe, dass mir hier jemand weiterhelfen kann.
    Ich habe mal probiert eine kleine Jump and Run Demo zu schreiben.
    Das ganze ist nur ein kleines Projekt. Vielleicht hilft es ja auch jemanden den es interessiert. ­čÖé
    Zum Problem:
    Wenn ich springe habe ich manchmal einen Pixel abstand zwischen dem Spieler und dem Boden. Ich wei├č es wird h├Âchst wahrscheinlich an der Kollisions abfrage liegen, aber ich habe irgendwie gerade ein Brett vorm Kopf.
    Vielleicht ist jemand so nett und ├╝berfliegt mal den Code. F├╝r Anregungen w├Ąre ich sehr dankbar.
    Ich bin reiner Hobbycoder daher hoffe ich das der Code einigerma├čen ordendlich geschrieben ist ­čśë
    Vielen Dank schonmal vorab!
    Guitarlo

    #include <iostream>
    #include<SDL2/SDL.h>
    struct _map
    {
        int tile=0;
        SDL_Rect xypos;
        bool pass=false;
    
    }level[25][25];
    
    struct _hero
    {
        int hy,hx;
        int xVel=0,yVel=0;
        SDL_Rect xypos,_xcolpos,_ycolpos;
    
    }Hero;
    struct _Collision
    {
        int xtile,ytile;
        bool xcollide=false,ycollide=false,up=false,down=false,left=false,right=false;
    
    }Collision;
    
    SDL_Renderer *rnd;
    SDL_Window *wnd;
    SDL_Event e;
    SDL_Texture *t_Hero,*t_wall,*t_Exit,*t_bg,*t_bg2;
    SDL_Rect r_hero, r_wall,tmp;
    float grav,speed;
    
    const int t_size=32;
    int movex;
    bool quit=false;
    bool jump=false;
    using namespace std;
    
    int Init();
    void Control();
    void CreateMap();
    void CreateTextures();
    bool XYCollision(SDL_Rect r1,SDL_Rect r2);
    void Scene();
    
    int main()
    {
        Init();
        CreateMap();
        CreateTextures();
        while (!quit)
        {
    
            Scene();
            Control();
            SDL_RenderPresent(rnd);
    
        }
    
        SDL_Quit();
        return 0;
    }
    
    //--------------------------------------------------------------------------------------------------------------------------
    
    int Init()
    {
        if (!SDL_Init(SDL_INIT_EVERYTHING))
            cerr << SDL_GetError()<<endl;
    
        wnd=SDL_CreateWindow("Jump and Run Test",NULL,NULL,800,600,0);
        if (wnd<0)
                cerr << SDL_GetError() << endl;
    
        rnd=SDL_CreateRenderer(wnd,NULL,SDL_RENDERER_ACCELERATED);
        if(rnd<0)
            cerr << SDL_GetError()<<endl;
    
        SDL_RenderPresent(rnd);
    
    }
    
    void Control()
    {
        while (SDL_PollEvent(&e))
        {
    
            speed=0;
            if(e.type==SDL_QUIT)
                quit=true;
    
            const Uint8 *key=SDL_GetKeyboardState(NULL);
            if(key[SDL_SCANCODE_ESCAPE])
                quit=true;
            if(key[SDL_SCANCODE_LEFT])
            {
                speed=-1;
    
            }
    
            if(key[SDL_SCANCODE_RIGHT])
            {
                speed=1;
    
            }
    
            if (!jump)
            {
                if(key[SDL_SCANCODE_LCTRL])
                {
                    grav=-4.5;
                    jump=true;
                }
            }
        }
    
    }
    void CreateMap()
    {
        for(int i=0;i<25;i++)
        {
            level[i][0].tile=1;
            level[i][18].tile=1;
            level[0][i].tile=1;
            level[24][i].tile=1;
        }
        level[4][2].tile=2;
        level[3][1].tile=1;
        level[3][2].tile=1;
        level[3][3].tile=1;
        level[4][3].tile=1;
    
        level[14][16].tile=1;
        level[7][11].tile=1;
        level[2][8].tile=1;
        level[8][7].tile=1;
        level[23][10].tile=1;
        level[19][14].tile=1;
        level[17][15].tile=1;
        level[16][6].tile=1;
        level[11][3].tile=1;
    
        Hero.hx=2;
        Hero.hy=2;
        Hero.xypos.x=Hero.hx*t_size;
        Hero.xypos.y=Hero.hy*t_size;
        Hero.xypos.w=t_size;
        Hero.xypos.h=t_size;
    
        for(int mx=0;mx<25;mx++)
        {
            for(int my=0;my<19;my++)
            {
                if (level[mx][my].tile>0)
                {
                    level[mx][my].xypos.x=mx*t_size;
                    level[mx][my].xypos.y=my*t_size;
                    level[mx][my].xypos.w=t_size;
                    level[mx][my].xypos.h=t_size;
                }
    
            }
        }
    
    }
    
    void CreateTextures()
    {
        t_Hero=SDL_CreateTexture(rnd,SDL_PIXELFORMAT_BGRA8888,SDL_TEXTUREACCESS_TARGET,256,256);
        t_wall=SDL_CreateTexture(rnd,SDL_PIXELFORMAT_BGRA8888,SDL_TEXTUREACCESS_TARGET,256,256);
        t_Exit=SDL_CreateTexture(rnd,SDL_PIXELFORMAT_BGRA8888,SDL_TEXTUREACCESS_TARGET,256,256);
        t_bg=SDL_CreateTexture(rnd,SDL_PIXELFORMAT_BGRA8888,SDL_TEXTUREACCESS_TARGET,256,256);
    
        for(int y=0;y<256;y++)
        {
            for(int x=0; x<256;x++)
            {
                SDL_SetRenderTarget(rnd,t_Hero);
                SDL_SetRenderDrawColor(rnd,255-y,0+x,0+y,255);
                SDL_RenderDrawPoint(rnd,x,y);
            }
        }
    
        for(int y=0;y<256;y++)
        {
            for(int x=0; x<256;x++)
            {
                SDL_SetRenderTarget(rnd,t_wall);
                SDL_SetRenderDrawColor(rnd,x^y,x^y,x^y,255);
                SDL_RenderDrawPoint(rnd,x,y);
            }
        }
        for(int y=0;y<256;y++)
        {
            for(int x=0; x<256;x++)
            {
                SDL_SetRenderTarget(rnd,t_Exit);
                SDL_SetRenderDrawColor(rnd,x^y,0,0,255);
                SDL_RenderDrawPoint(rnd,x,y);
            }
        }
    
        for(int y=0;y<256;y++)
        {
            for(int x=0; x<256;x++)
            {
                SDL_SetRenderTarget(rnd,t_bg);
    
                SDL_SetRenderDrawColor(rnd,10,10,y,0);
                SDL_RenderDrawPoint(rnd,x,y);
    
            }
        }
        t_bg2=t_bg;
    
            SDL_SetRenderTarget(rnd,NULL);
    
    }
    bool XYCollision(SDL_Rect r1,SDL_Rect r2)
    {
    
        if( r1.x+r1.w <= r2.x )
        {
            return false;
        }
    
        if( r1.x >= r2.x+r2.w )
        {
            return false;
        }
        if( r1.y+r1.h <= r2.y )
        {
            return false;
        }
    
        if( r1.y >= r2.y+r2.h)
        {
            return false;
        }
    
        return true;
    }
    
    void Scene()
    {
        jump=true;
        SDL_SetRenderDrawColor(rnd,0,0,0,255);
        SDL_RenderClear(rnd);
        SDL_RenderCopy(rnd,t_bg,NULL,NULL);
    
        grav+=0.04;
        if (grav>2)
            grav=2;
    
        Hero.hx=int(Hero.xypos.x/t_size);
        Hero.hy=int(Hero.xypos.y/t_size);
        Hero.yVel=(int)grav;
        Hero.xVel=(int)speed;
    
        Hero._xcolpos.x=int(Hero.xypos.x+Hero.xVel);
        Hero._ycolpos.y=int(Hero.xypos.y+Hero.yVel);
    
        for(int mx=0;mx<25;mx++)
        {
            for(int my=0;my<25;my++)
            {
                if(int(mx*32)<800 && int(my*32)<600)
                {
                    if (level[mx][my].tile==1)
                    {
                        tmp=level[mx][my].xypos;
                        SDL_RenderCopy(rnd,t_wall,NULL,&tmp);
                    }
                    if (level[mx][my].tile==2)
                    {
                        tmp=level[mx][my].xypos;
                        SDL_RenderCopy(rnd,t_Exit,NULL,&tmp);
                    }
    
                    if (!Collision.ycollide)
                    {
    
                        Collision.ycollide=XYCollision(Hero._ycolpos,level[mx][my].xypos);
                        if(Hero.yVel>0)Collision.down=true;
                        Collision.ytile=level[mx][my].tile;
                    }
    
                    if (!Collision.xcollide)
                    {
    
                        Collision.xcollide=XYCollision(Hero._xcolpos,level[mx][my].xypos);
                        Collision.xtile=level[mx][my].tile;
                    }
    
                }
            }
        }
    
        if (Collision.ycollide)
        {
            if(grav<0) grav=0;
            Hero._ycolpos.y=Hero.xypos.y;
            if (Collision.down)jump=false;
    
        }
    
        if (Collision.xcollide)
        {
            Hero._xcolpos.x=Hero.xypos.x;
            if(Collision.xtile==2)
            {
                cout << "Level Ziel erreicht!" <<endl<<"Danke f├╝rs Spielen!";
                quit=true;
            }
    
        }
    
        Hero.xypos.x=Hero._xcolpos.x;
        Hero.xypos.y=Hero._ycolpos.y;
        SDL_RenderCopy(rnd,t_Hero,NULL,&Hero.xypos);
    
        Collision.ycollide=false;
        Collision.xcollide=false;
        Collision.down=false;
        Collision.up=false;
        Collision.left=false;
        Collision.right=false;
    
        Hero._xcolpos=Hero.xypos;
        Hero._ycolpos=Hero.xypos;
    
    }
    

  • Mod

    guitarlo schrieb:

    Zum Problem:
    Wenn ich springe habe ich manchmal einen Pixel abstand zwischen dem Spieler und dem Boden.

    Ist nicht der ganze Sinn vom Springen, Abstand vom Boden zu erreichen?



  • Das ist richtig. Aber komischerweise bleibt der Spieler bei der Landung 1 Pixel ├╝ber dem Boden stehen. So eckt schonmal eine obere Ecke an dar├╝berliegende block an wenn man zur Seite l├Ąuft. Ich glaube ich sehe gerade den Wald vor lauter B├Ąumen nicht ­čÖé


  • Mod

    Wohin setzt du den Spieler denn, wenn es eine kolision gab?



  • guitarlo schrieb:

    Ich bin reiner Hobbycoder daher hoffe ich das der Code einigerma├čen ordendlich geschrieben ist ­čśë

    Schalt noch die Warnungen im Compiler ein.



  • rapso schrieb:

    Wohin setzt du den Spieler denn, wenn es eine kolision gab?

    Ich addiere in der Mainschleife jedesmal die y Position mit der y Velocity. Also in dem Falle der Schwerkraft. Also ich hatte mir das so vorgestellt:

    mainschleife
    {
    y position + velocity= neue position
    ycollisions abfrage der neuen Position
    Wenn die neue Posiotion mit dem Boden kollidiert, wird die alte Position beibehalten.
    Findet keine Kollision statt, wird die neue Position ├╝bernommen.
    }

    Und aus irgendeinem Grund bleibt bei jedem 2. Sprung der Player 1 Pixel ├╝ber'n boden und kollidiert somit mit freischwebenden Bl├Âcken.



  • guitarlo schrieb:

    coder daher hoffe ich das der Code einigerma├čen ordendlich geschrieben ist ­čśë

    Keine Klassenstruktur, globale Variablen, wilder Mischmasch ziwschen float und int => Da ist noch viel Luft nach oben.

    Warum setzt du in Scene() jump = true? Willst du immer springen? Warum erh├Âst du in Scene() grav immer um 0,4?

    guitarlo schrieb:

    mainschleife
    {
    y position + velocity= neue position
    ycollisions abfrage der neuen Position
    Wenn die neue Posiotion mit dem Boden kollidiert, wird die alte Position beibehalten.
    Findet keine Kollision statt, wird die neue Position ├╝bernommen.
    }

    Also hei├čt Kollision mit dem Boden => letzte Position wo noch keine Kollision stattgefunden hat? Und du ├╝berpr├╝fst das immer Pixelweise? Kann es nicht sein, dass die letzte gepr├╝fte Position eben 1 Pixel ├╝ber dem Boden ist, wenn die aktuelle gepr├╝fte Position schon im Boden ist?



  • Ja stimmt, auf Klassen habe ich in diesem Beispiel komplett verzichtet.
    W├Ąre ├╝bersichtlicher, hast du recht. Sorry.

    Ja die Sache mit der Gravity ist mir auch aufgefallen. Die Gravity ist jetzt konstant. Hab sie im obrigen Quelltext f├Ąlschlicherweise anstatt yVelocity benutzt.

    Wenn der Player springt wurde die grav auf -4.5 gesetzt und dann beim auf +2 hochgez├Ąhlt damit er wieder f├Ąllt. Somit bekommt jetzt die yVelocity einen wert von -4.5 (fand ich als Sprunggeschwindigkeit okay ;)) und die Gravity wir addiert so dass der Player wieder f├Ąllt.

    Vor der Kollisionsabfrage ist jump auf true. Findet keine Bodenkollision statt wird jump auf false gesetzt. Sollte ich das anders machen?

    Um die Bewegung des Spielers zu verlangsamendie habe ich float benutzt. So konnte ich die Nachkommastellen addieren. Somit wird auf SDL_Rect die xVelocity gerundet und der Spieler ist langsamer. H├Âchstwahrscheinlich gibt es da eine bessere m├Âglichkeit.

    Schlangenmensch schrieb:

    Also hei├čt Kollision mit dem Boden => letzte Position wo noch keine Kollision stattgefunden hat? Und du ├╝berpr├╝fst das immer Pixelweise? Kann es nicht sein, dass die letzte gepr├╝fte Position eben 1 Pixel ├╝ber dem Boden ist, wenn die aktuelle gepr├╝fte Position schon im Boden ist?

    Guter Ansatz aber komischer weise ist der Pixel versatz nur nach jedem 2. Sprung drin.

    Aber schonmal danke f├╝r die Hilfe! ­čÖé


  • Mod

    guitarlo schrieb:

    rapso schrieb:

    Wohin setzt du den Spieler denn, wenn es eine kolision gab?

    Ich addiere in der Mainschleife jedesmal die y Position mit der y Velocity....

    Wenn die neue Posiotion mit dem Boden kollidiert, wird die alte Position beibehalten.
    ..

    wie weit ist die alte position entfernt vom boden?



  • Ein Pixel da es pixelweise abgefragt wird.
    Das wunderliche ist, dass es nur bei jedem 2. Sprung so ist.


  • Mod

    guitarlo schrieb:

    Ein Pixel da es pixelweise abgefragt wird.

    if (grav>2)
            grav=2;
    ...
        Hero.yVel=(int)grav;
    ...
        Hero._ycolpos.y=int(Hero.xypos.y+Hero.yVel);
    ...
        if (Collision.ycollide)
            Hero._ycolpos.y=Hero.xypos.y;
    

    Das wunderliche ist, dass es nur bei jedem 2. Sprung so ist.

    setz einen breakpoint wo kollision festgestellt wird und loese die verweunderung auf ­čśë



  • Okay danke ­čśë


Log in to reply