Anfängerfrage zu Sprites und DoubleBuffering

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
CptGreenwood
Beiträge: 125
Registriert: 12.07.2007 10:40

Anfängerfrage zu Sprites und DoubleBuffering

Beitrag 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
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
jojo1541
Beiträge: 431
Registriert: 15.09.2007 17:12
Wohnort: Irgendwo im Nirgendwo

Beitrag 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. <)
Ich verkaufe Rechtschreibfehler und jede menge GROßBUCHSTABEN. Alles unbegrenzt zu haben.
Benutzeravatar
CptGreenwood
Beiträge: 125
Registriert: 12.07.2007 10:40

Beitrag 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?
Rokur
Beiträge: 167
Registriert: 29.12.2005 09:58
Computerausstattung: Intel Core2 Quad (4x2,4 GHz), 4096 MB RAM, GForce 8800GTX 786 MB
Windows XP 32 Bit, PureBasic 4.40 (x86)

Beitrag 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.
Benutzeravatar
CptGreenwood
Beiträge: 125
Registriert: 12.07.2007 10:40

Beitrag 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?
Rokur
Beiträge: 167
Registriert: 29.12.2005 09:58
Computerausstattung: Intel Core2 Quad (4x2,4 GHz), 4096 MB RAM, GForce 8800GTX 786 MB
Windows XP 32 Bit, PureBasic 4.40 (x86)

Beitrag von Rokur »

Das ist ganz schlechter Stil, denn sobald Teile des Fensters/Screens überdeckt wurden, werden die nicht mehr neu gezeichnet.
Rokur
Beiträge: 167
Registriert: 29.12.2005 09:58
Computerausstattung: Intel Core2 Quad (4x2,4 GHz), 4096 MB RAM, GForce 8800GTX 786 MB
Windows XP 32 Bit, PureBasic 4.40 (x86)

Beitrag 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)
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Beitrag 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.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
CptGreenwood
Beiträge: 125
Registriert: 12.07.2007 10:40

Beitrag 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.
Antworten