Konstante Bewegung
-
Mit C++ hab ich eher noch nicht so lange angefangen... Ich will jetzt ein Programm schreiben, mit dem man ein Objekt auf dem Bildschirm mithilfe der Pfeiltasten bewegen kann. Das hab ich eigentlich schon gemacht, bis auf das konstante Weiterbewegen beim Halten der Tasten.
Wie kann ich es so programmieren, damit sich das Bild beim Halten der Pfeiltaste z.B. 10 Pixel jede 10tel-Sekunde bewegt? Da muss ich doch die Bewegungen der Framerate anpassen, doch wie geht das genau?
Anmerkung: So, wie der Quelltext hier steht, läuft das Programm. Ich kann mit den Pfeiltasten die Position des Bildes ändern, jedoch muss ich sie immer erneut drücken.#include <SDL.h> #include <SDL_image.h> int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); atexit(SDL_Quit); SDL_Surface *screen, *bild; screen = SDL_SetVideoMode(640,480,16,SDL_HWSURFACE); bild = IMG_Load("Pfeil.gif"); int a = 135; SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, a, a, a)); SDL_Rect zielR; zielR.x = 100; zielR.y = 100; zielR.h = bild->h; zielR.w = bild->w; SDL_BlitSurface(bild,0,screen,&zielR); SDL_UpdateRect(screen,0,0,0,0); int screenW = screen->w; int screenH = screen->h; Uint8 *Tasten; bool laufend = true; while (laufend) { Tasten = SDL_GetKeyState(0); SDL_Event ereignis; SDL_MouseButtonEvent Maus; while(SDL_PollEvent(&ereignis)) { switch(ereignis.type) { case SDL_QUIT: laufend = false; break; case SDL_MOUSEBUTTONDOWN: if (Maus.button == SDL_BUTTON_LEFT) { zielR.x = Maus.x; zielR.y = Maus.y; } break; case SDL_KEYDOWN: if (ereignis.key.keysym.sym == SDLK_ESCAPE) // hier hab ich mal beide Varianten ausprobiert... laufend = false; if (Tasten[SDLK_UP] && zielR.y>0) // das wäre die zweite... zielR.y-=10; if (Tasten[SDLK_DOWN] && zielR.y<screenH-zielR.h) zielR.y+=10; if (Tasten[SDLK_LEFT] && zielR.x>0) zielR.x-=10; if (Tasten[SDLK_RIGHT] && zielR.x<screenW-zielR.w) zielR.x+=10; break; } SDL_BlitSurface(bild,0,screen,&zielR); SDL_UpdateRect(screen,0,0,0,0); SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, a, a, a)); } } }Ausserdem hab ich noch ein anderes Problem: Bei einem Mausklick soll sich das Objekt an den geklickten Ort verschieben. Was hab ich da falsch gemacht?
Es kann auch sein, dass sonstige grobe Fehler oder schlechte Performance vorkommen. Bitte sagt mir auch das... Ich sehe solche Sachen nicht
Ich wäre sehr froh um eure Hilfe!
-
ich würd mir einfach immer anschauen, wie spät es ist und alle 10 ms die Figur um 1 Pixel verschieben..
Du merkst dir einfach beim Losgehen den aktuellen Zeitstempel jeden Durchlauf schaust du, ob der aktuelle Zeitstempel / 10 größer ist als der gemerkte / 10. Wenn ja, bewegst du das Objekt und speicherst den neuen Zeitstempel...
-
Ich wür das ganze ewas weicher Programmieren, und für Positionen Fließkommazahlen nehmen. Dann ein Zeitmesser einbauen und die Vergangene Zeit seit dem Letzten Frame in einer Variablen speichern. Dann kannst du die Zeit mit dem dem Geschwindigkeitsfaktor Multiplizieren, und auf die Position draufaddieren. dann Kann auch mal die Framerate schwanken, dein gerät bleibt gleich schnell. Die Werta kannst du die ja dann selber ausrechnen/testen. Habs selber noch nicht ungesetzt bin selbst noch anfänger, kann dir Also den genauen Namen der Funtion zu zeitmessen nicht sagen.
Die X/Y Koordinaten musst du dann natürlich ausserhalb des rect speichern. wenn du schon C++ verwendest, könntest du auch versuchen das ganze etwas zu verkapseln, also eine Klasse schreiben die Objekt oder so heist mit den Methoden move draw etc, und die IMG_Load FreeSurface und so in Construktor und Destruktor hat. Das schafft Übersicht.
PS:
dies dürfte dein Problem mit den Testen beheben:
http://www.libsdl.org/pipermail/sdl/1999-March/020479.htmlHier bekommst du ein Array mit den Informationen über alle gedrückten tasten. Tasten die Gedrückt sind haben den status SDL_PRESSED, und wenn du ein Wenig über Enums weist, müsstest du wissen, das das nicht weiter als ein Integer ist (wahrscheinlich 1), und da ja bekanntlich alle integer ausser 0 zu true werden, ist diese abfrage == SDL_PRESSED völlig überflüssig. es reich also if (tasten[SDLK_äh])
-
Im Grunde ganz simpel und das da oben leitet sich alles davon ab, wenn man in eine spezielle Richtung denkt.
solange Programm laeuft zeichne Bild an Position wenn Taste gedrueckt dann setze Position abhängig von der Zeitf'`8k
Gruß, TGGC (\-/ has leading)
-
Okay danke vielmals für eure Hilfe...
-
Mir ist gerade ein neues Problem gekommen... Ich hab hier das Programm von vorher ein bisschen verändert(z.b. Maus integriert).
Mit den vier Pfeiltasten kann man das Objekt auf dem Bildschirm bewegen... Ich hab auch gemacht, dass bei 2 Pfeiltasten gleichzeitig diagonal bewegt wird (Ich weiss, ich hätte das nicht machen müssen, wär auch sonst gegangen, aber auf diese Art kann ich die diagonale und vertikale/horizontale Geschwindigkeit anpassen).
Doch jetzt hab ich ein neues Problem: Wenn ich zuerst die linke Pfeiltaste drücke, dann die die obere auch noch (linke wird währenddessen gehalten), fährt das Bild diagonal nach links oben. Wenn ich jetzt aber wieder die obere loslasse (nur noch linke gedrückt), sollte es wieder ohne Unterbruch nach links fahren, stattdessen bleibt es am Ort stehen.
Wie kann ich es machen, dass die Tastenfolge LINKS -> LINKS+OBEN -> LINKS das Bild ohne Unterbruch bewegen kann?#include <SDL.h> #include <SDL_image.h> int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); // Initialisierung SDL atexit(SDL_Quit); SDL_Surface *screen, *bild; screen = SDL_SetVideoMode(640,480,16,SDL_HWSURFACE); bild = IMG_Load("Pfeil.gif"); int a = 135; SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, a, a, a)); //grauer Hintergrund SDL_Rect zielR; zielR.x = 100; zielR.y = 100; zielR.h = bild->h; // Grösse des Bildes zielR.w = bild->w; // SDL_BlitSurface(bild,0,screen,&zielR); SDL_UpdateRect(screen,0,0,0,0); Uint8 *Tasten; SDL_EnableKeyRepeat(20,20); // aktiviert Tastenwiederholung beim Halten unsigned short int DiagonalVerschiebung = 6; // Geschwindigkeit in diagonaler Richtung unsigned short int HorzVertVerschiebung = 9; // horizontale und vertikale Geschwindigkeit bool laufend = true; while (laufend) { Tasten = SDL_GetKeyState(0); SDL_Event Ereignis; while(SDL_PollEvent(&Ereignis)) { switch(Ereignis.type) { case SDL_QUIT: // [X] Fenster schliessen laufend = false; break; case SDL_MOUSEBUTTONDOWN: // Reaktion bei Mausklick switch (Ereignis.button.button) { case SDL_BUTTON_LEFT: // linke Maustaste zielR.x = Ereignis.button.x - static_cast<int>((bild->w)/2); zielR.y = Ereignis.button.y - static_cast<int>((bild->h)/2); break; case SDL_BUTTON_WHEELUP: // Mausrad nach oben HorzVertVerschiebung += 3; DiagonalVerschiebung += 2; break; case SDL_BUTTON_WHEELDOWN: // Mausrad nach unten HorzVertVerschiebung -= 3; DiagonalVerschiebung -= 2; break; } break; case SDL_KEYDOWN: // Escape -> Fenster schliessen if (Ereignis.key.keysym.sym == SDLK_ESCAPE) laufend = false; // nach oben und links if (Tasten[SDLK_UP] && Tasten[SDLK_LEFT] && zielR.y>0 && zielR.x>0) { zielR.y-=DiagonalVerschiebung; zielR.x-=DiagonalVerschiebung; break; } // nach oben und rechts if (Tasten[SDLK_UP] && Tasten[SDLK_RIGHT] && zielR.y > 0 && zielR.x < screen->w - bild->w) { zielR.y-=DiagonalVerschiebung; zielR.x+=DiagonalVerschiebung; break; } // nach unten und links if (Tasten[SDLK_DOWN] && Tasten[SDLK_LEFT] && zielR.y < screen->h - bild->h && zielR.x > 0) { zielR.y+=DiagonalVerschiebung; zielR.x-=DiagonalVerschiebung; break; } // nach unten und rechts if (Tasten[SDLK_DOWN] && Tasten[SDLK_RIGHT] && zielR.y < screen->h - bild->h && zielR.x < screen->w - bild->w) { zielR.y+=DiagonalVerschiebung; zielR.x+=DiagonalVerschiebung; break; } // nur nach oben if (Tasten[SDLK_UP] && zielR.y>0) { zielR.y-=HorzVertVerschiebung; break; } // nur nach unten if (Tasten[SDLK_DOWN] && zielR.y < screen->h - bild->h) { zielR.y+=HorzVertVerschiebung; break; } // nur nach links if (Tasten[SDLK_LEFT] && zielR.x>0) { zielR.x-=HorzVertVerschiebung; break; } // nur nach rechts if (Tasten[SDLK_RIGHT] && zielR.x < screen->w - bild->w) { zielR.x+=HorzVertVerschiebung; break; } break; } if (zielR.x > screen->w - bild->w) // Verschwinden aus Bildschirm zielR.x = screen->w - bild->w; // wird hier verhindert if (zielR.y > screen->h - bild->h) zielR.y = screen->h - bild->h; if (HorzVertVerschiebung < 3) // Verhinderung von zu kleinen HorzVertVerschiebung = 3; // Geschwindigkeiten if (DiagonalVerschiebung < 2) DiagonalVerschiebung = 2; SDL_BlitSurface(bild,0,screen,&zielR); SDL_UpdateRect(screen,0,0,0,0); SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, a, a, a)); // grauer Hintergrund } } }
-
du solltest evtl. die Framerate irgendwie limitieren (zumindest wenn du mit integer Positionen ohne Fixkommaanteil rechnest)
du solltest die Zeit sei dem letzten Frame nehmen und mit der Geschwindigkeit deiner Objekte multiplizieren, und das pro Frame auf die Position draufaddieren
normalerweise geht man her und merkt sich für die 4 Cursortasten je ein Flag das den aktuellen Zustand wiedergibt. Bei SDL bekommst du das sogar "gratis" über SDL_GetKeyState.
Dann bestimmst du pro Frame die aktuelle Richtung einfach so:Tasten = SDL_GetKeyState(0); int dirX = 0; int dirY = 0; if(Tasten[SDLK_UP]) dirY--; if(Tasten[SDLK_DOWN]) dirY++; if(Tasten[SDLK_LEFT]) dirX--; if(Tasten[SDLK_RIGHT]) dirX++;Dann hast du mit (dirX, dirY) quasi den "Vektor" für die Cursortasten. Noch dazu berücksichtigt der Code automatisch den Fall wenn jmd. gleichzeitig rauf und runter drückt (dirY wird wieder 0) oder eben links und rechts gleichzeitig.
Wenn du jetzt diagonal langsamer bewegen willst als horizontal/vertikal machst du das einfach so:
int speed = 9; if(dirX && dirY) speed = 6; // diagonalUnd bewegen tust du deine Objekte dann z.B. so:
object.x += dirX * speed * frameTime; object.y += dirY * speed * frameTime;Irgendwelchen komplizierten if-Orgien kannst du dir dabei sparen

Und wenn du schon SDL_GetKeyState() verwendest kannst du dir im Prinzip auch den ganzen "case SDL_KEYDOWN:" sparen. Wenn du Beginn und Ende einer Bewegung (key down und key up) mitbekommen willst wäre es sowieso besser dir die alten Werte von dirX und dirY zu merken, und dann pro Frame einfach die neuen mit den alten Werten zu vergleichen.
Noch dazu entkoppelst du dadurch etwas das "wo kommt mein Input her" vom "was fange ich damit an". Wos herkommt ist dann nämlich egal solange es immer in dirX und dirY landet. Sowas wie nachträglich Joystick Input einbauen wird dadurch viel einfacher.Für Koordinaten bietet sich im übrigen float an, oder ggf. noch int mit 1^16 skaliert oder so. Auf jeden Fall etwas wo du effektiv Nachkommastellen hast.
Die einzige Möglichkeit sinnvoll ohne Nachkommastellen zu arbeiten ist wenn du die Framerate fixierst, bloss dann sollte es auch nicht langsamer werden, und "gegen langsamer" kannst du im Normalfall nix tun.
-
Danke für die vielen Hinweise! Doch ich begreif nicht ganz, wieso Floats besser geeignet sind als Integers, zumal nur ganze Pixel gezeichnet werden können (oder seh ich das falsch?) Und wieso wirken sich Floats besser auf die Framerate aus?...
Im Moment geht es eigentlich noch mit der Framerate... Man sieht von Auge überhaupt nichts... Aber ich kann mir vorstellen, dass das bei grösseren Programmen zu einem Problem werden kann.