Seite 1 von 2

Anfängerfrage zu Sprites und DoubleBuffering

Verfasst: 04.06.2009 16:44
von CptGreenwood
Hallo,

ich versuche seit Stunden verzweifelt ein eigentlich ganz einfaches Szenario hinzubekommen. Ich habe mit OpenScreen einen Grafikbildschirm geöffnet, mit LoadImage und DrawImage ein Hintergrundbild angezeigt und das Ganze mit FlipBuffer und ClearScreen in eine Schleife gelegt, die das Prog beendet, wenn man Escape drückt.
Das klappt auch gut.
Nun möchte ich bei einem Mausklick in der Mitte des Bildschirms eine Art Fenster anzeigen (die Grafik für das Fenster ist ein Sprite) und dann innerhalb einer Schleife mit 2 Sek. Abstand (Delay(2000)) einige weitere Sprites auf diesem Fenstersprite anzeigen und anschließend warten, bis eine Taste gedrückt wird, dann soll Fenstersprite samt allen anderen Sprites verschwinden und nur noch das Hintergrundbild sichtbar sein.

Ich habe es bisher mit keinem einzigen Versuch hinbekommen, was vielleicht daran liegt, dass ich das DoubleBuffering nicht 100%ig durchblicke. Entweder das Hintergrundbild ist weg, wenn ich die Maustaste drücke oder beim Anzeigen der Sprites auf dem Fenster (innerhalb Schleife) wird immer nur das aktuelle angezeigt, alle davor verschwinden oder ich sehe nur das Fenster mit dem allerletzen Sprite oder überhaupt nichts (kein Fenster, keine Sprites) und erst wenn ich Escape drücke sehe ich alles ganz kurz. bevor es verschwindet. Ich bekomme es einfach nicht hin!

Kann mir jemand, der mehr Erfahrung mit der 2D-Grafik hat kurz skizzieren, wann ich was machen muss? Wo brauche ich welche Schleifen? Wo muss ClearScreen und wo FlipBuffer stehen?

Wäre echt dankbar für Hilfe...

Greetz,
Cpt

Verfasst: 04.06.2009 18:03
von Kaeru Gaman
eine Display-Schleife für nen Screen sollte IMMER permanent durchlaufen.

also, wenn du zwei sekunden lang was anzeigen willst, musst du
a) deine Frames mitzählen, bis zwei sekunden rum sind.
b) nen timer mitlaufen lassen.

du kanst für den anfang SetFrameRate() verwenden, oder du baust gleich nen timer ein.

hier mal zwei alte beispiele, wie screenhändling aussehen kann:
http://www.purebasic.fr/german/viewtopi ... 539#136539
http://www.purebasic.fr/german/viewtopi ... 634#116634

die sind auch etwas älter, also vielleicht musst du auch noch was anpassen...

Verfasst: 05.06.2009 02:23
von jojo1541

Code: Alles auswählen

InitSprite()
InitKeyboard()
InitMouse()

fa.l ;F(enster)A(ktiv)

OpenScreen(1024,768,32,"Screenhandling")

CreateSprite(0,1024,768) ;Hintergund

  StartDrawing(SpriteOutput(0))
    For y = 1 To 768
      Line(0,y,1024,0,RGB(255*y/768,0,0))
    Next
  StopDrawing()


CreateSprite(1,400,400) ;Fenster

  StartDrawing(SpriteOutput(1))
    Box(0,0,400,400,RGB(132, 212, 232))
  StopDrawing()
  

CreateSprite(2,32,32) ; Button1

StartDrawing(SpriteOutput(2))
  Box(0,0,32,32,RGB(100,200,100))
StopDrawing()

CreateSprite(3,32,32) ; Button2

StartDrawing(SpriteOutput(3))
  Box(0,0,32,32,RGB(100,100,100))
StopDrawing()

CreateSprite(4,4,4) ; Maus

  StartDrawing(SpriteOutput(4))
    Circle(2,2,2,RGB(0,255,0))
  StopDrawing()
TransparentSpriteColor(4,0)

Repeat

ExamineKeyboard()
ExamineMouse()

ClearScreen(0)

DisplaySprite(0,0,0) ; hintergrund



If MouseButton(#PB_MouseButton_Left) And mp = 0; mp = mouse pressed
  fa = 1
  timer = ElapsedMilliseconds()
  mp = 1
EndIf

If fa = 1
  
  DisplaySprite(1,312,159) ; Fenster anzeigen
  
  
  
  If (ElapsedMilliseconds() - timer)/1000 = 2 ; nach zwei Sekunden
    ds = 1
  EndIf
  If (ElapsedMilliseconds() - timer)/1000 = 4 ; nach vier Sekunden
    ds = 2
  EndIf
  
  If ds = 1
    DisplaySprite(2,322,169) ; nach zwei sekunden ds = 1 wird nur das erste angezeigt
  EndIf
   
  If ds = 2                 ; Nach vier sekunden das zweite zusammen mit dem ersten
    DisplaySprite(2,322,169)
    DisplaySprite(3,364,169)
  EndIf

EndIf

DisplayTransparentSprite(4,MouseX(),MouseY()) ; Maus

FlipBuffers()

Until KeyboardPushed(#PB_Key_Escape)
Hier mal ein kleines Beispiel.

Schlagt mich nicht, wenn es eigentlich besser geht, es ist halb drei morgens und ich penn schon vorm Pc. <)

Verfasst: 05.06.2009 08:33
von CptGreenwood
Drei Uhr am PC? Arghh. :freak:

Vielen Dank für die Antworten. Ich hab mir die Codeschnipsel mal angeschaut und bin zur Erkenntnis gekommen, dass ich es grundsätzlich falsch anpacke. Ich habe die Sprites auf dem Fenster in einer For-Schleife angezeigt, in der ich jeweils ein FlipBuffers untergebracht habe, die zwei Sekunden habe ich in der Schleife mit Delay realisiert. Ich werde es mal mit einer permanenten Schleife und dem Timer versuchen.

Zum Verständnis:
DoubleBuffering nimmt man dann, wenn sich Dinge auf dem Bildschirm bewegen sollen, damit Flackern vermieden wird, gell? Da sich bei mir aber nichts bewegt, sondern lediglich neue Elemente auf das aktuelle Bild drauf gezeichnet werden, könnte man da nicht sagen, dass man für diese Routine auf DoubleBuffering verzichtet und seinen Kram direkt in den FrontBuffer schreibt? Gab es da nicht eine Möglichkeit, zu bestimmen, ob die Ausgabe auf dem Front- oder dem BackBuffer gemacht werden soll?

Verfasst: 05.06.2009 10:30
von Rokur
Doublebuffering ist auch bei unbeweglichen Grafikausgaben zu empfehlen.
Nehmen wir an deine Hauptschleife sieht so aus:
- Bildschirm löschen, damit wäre z.B. alles schwarz
- Hintergrund zeichnen
- verschiedene Objekte zeichnen
- evtl. noch Benutzereingaben abfragen und darauf reagieren

Wenn du das jetzt ohne Doublebuffering machst, dann kann das bei wenigen Elementen noch klappen ohne das du was siehst, aber je mehr Elemente du anzeigst, desto mehr flimmert dein Programm, auch wenn sich nichts bewegt.
Der Grund:
Alles was du direkt auf den sichtbaren Bereich zeichnest ist sofort zu sehen. D.h. für den Bruchteil einer Sekunde ist der Bildschirm schwarz, dann ist nur das Hintergrundbild zu sehen, dann der Hintergrund und das erste Element, dann Hintergrund und die ersten beiden Elemente, usw. bis die ganze Grafikausgabe fertig ist. Danach beginnt alles wieder mit dem schwarzen Bildschirm.
Die zuletzt gezeichneten Elemente sind damit am kürzesten zu sehen und flimmern irgendwann. Sehr deutlich kann man das z.B. bei Spielen sehen, deren Hintergrund aus Tiles aufgebaut ist.
Beim Doublebuffering zeichnest du zuerst alles auf einen nicht sichtbaren Bereich und kopierst diesen dann auf einmal in den sichtbaren Bereich. Jetzt kannst du im nicht sichtbaren Bereich das Bild neu aufbauen, während sich der sichtbare Bereich in der Zwischenzeit nicht verändert.

Doublebuffering kann sogar sinnvoll sein wenn du auf ganz normale Fenster zeichnest (egal ob in einer Schleife oder im Repaint-Event), nur das du es dir da selber bauen musst, da FlipBuffers() nur mit Screens funktioniert.

Verfasst: 05.06.2009 10:50
von CptGreenwood
Hmm... soweit verstanden. Aber was ist, wenn ich gar keine Hauptschleife habe und den Bildschirm damit auch nicht ständig lösche. Also etwa so (Ausgaben immer direkt auf den FrontBuffer):

1. Bildschirm löschen
2. Hintergrundbild anzeigen
3. Fenster anzeigen
4. For-Scheife
4a. Sprite n ins Fenster zeichnen
4b. Next
5. Repeat
5a. Until Tastendruck
6. Return

Habe ich denn korrekt in Erinnerung, dass es einen Befehl gibt, die Ausgabe direkt auf den FrontBuffer zu machen?

Verfasst: 05.06.2009 11:00
von Rokur
Das ist ganz schlechter Stil, denn sobald Teile des Fensters/Screens überdeckt wurden, werden die nicht mehr neu gezeichnet.

Verfasst: 05.06.2009 11:14
von Rokur
Bezogen auf dein Beispiel sollte das dann so aussehen:

Code: Alles auswählen

Repeat
  Delay(1) ;wegen CPU-Auslastung
  FlipBuffers()
  ClearScreen(#Black)
  ;Hintergrundbild zeichnen
  ;Fenster zeichnen
  For n = 0 To anzahlSprites -1
    ;Sprite n zeichnen
  Next n
  ExamineKeyboard()
Until KeyboardPushed(#PB_Key_Escape)

Verfasst: 05.06.2009 11:25
von Kurzer
Dieses permanente Neuzeichnen des Screens in einer Schleife habe ich als alter Amiga-Rentner auch nicht auf Anhieb begreifen (wollen).
Ökologisch ist das nicht, PC und Grafikkarte sind ständig am rödeln, auch wenn nur statische Grafik angezeigt wird.

Eine Möglichkeit nur auf dem Frontbuffer zu zeichenen wäre in manchen Fällen gar nicht mal so schlecht. Zumal es im Screen-Modus auch nicht zu Überlappung mit einem Fenster kommen kann.

Als Beispiel fällt mir hier das langsame Zeichenen eines Graphen in einem Koordinatensystem ein.

Verfasst: 05.06.2009 12:21
von CptGreenwood
Ich habe die Hilfe mal durchforstet. Es scheint keine Möglichkeit in PB zu geben, direkt auf den Frontbuffer zu zeichnen. Schade eigentlich. Wie mein "Vorredner" schon sagt, alle Vorgänge die "langsam" ablaufen sollen, sind mit DoubleBuffering scheinbar ziemlich kompliziert.

@Rokur:
Dein Code würde sicherlich funktionieren, aber es wäre "flupp" alles auf einmal da. Es soll in der Spriteschleife ja der erste erscheinen, 2 Sek Pause, dann der zweite, 2 Sek Pause, dann der Dritte, u.s.w. Daran scheitere ich, das funktioniert nicht mit nem Delay, vielleicht wirklich nur über einen Timer.