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; }
-
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
-
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!
-
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.
-
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