Timer, Millisecs() und zeitliche Abläufe für Anfänger

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

Midimaster

Betreff: Timer, Millisecs() und zeitliche Abläufe für Anfänger

BeitragMi, Apr 07, 2010 9:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Timer, Millisecs() und zeitliche Abläufe für Anfänger

Dies ist ein Tutorial für blutige Anfänger. Es erklärt wie Spiel-Elemente eine Kette von Ereignissen durchleben können und der Rest des Spiels trotzdem weiterläuft. z.B. Explosionen, Sprünge, etc...

(Ich bitte alle, sich mit eigenen Einträgen rauszuhalten und dafür lieber den Thread "Kritik an Midimaster-Tutorials" zu nutzen. Dieses Tutorial soll den Anfänger möglichst durch keine Diskussionen irritieren.) Kritik hier hin: Kritik-Thread

Allen Anfängern lege ich wärmstens ans Herz dieses Tutorial durchzuarbeiten. Es spart euch viele Fehler in späteren Programmen. Fragen von Hilfesuchenden bitte ins BB-Beginners-Corner ( https://www.blitzforum.de/forum/viewforum.php?f=18 ), aber auf keinen Fall hier hin posten!

Das Tutorial ist didaktisch konzipiert aufgebaut. Die Lektionen bauen aufeinander auf. Die Befehle werden nie vollständig erklärt, sondern immer nur soweit man es für diese Lektion unbedingt braucht.


Lektion I: Wohin führt dieses Tutorial? Arrow Lektion I

Lektion II: CreateTimer() oder Millisecs()? Arrow Lektion II

Lektion III: Frames, Stoppuhr, Schilder. Arrow Lektion III

Lektion IV: mehrere Timer und Flags Arrow Lektion IV

Lektion V: Ausblicke Arrow
  • Zuletzt bearbeitet von Midimaster am So, Mai 02, 2010 9:20, insgesamt 2-mal bearbeitet

Midimaster

Betreff: Lektion I: Wohin führt dieses Tutorial?

BeitragMi, Apr 07, 2010 9:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion I: Wohin führt dieses Tutorial?

Immer wieder scheitern neue Blitz-User an dem Problem mitten im laufenden Spiel eine der Spielfiguren eine länger dauernde Aktion durchführen zu lassen. Folgende Aktionen fallen wir dabei ein:

A.
Eine Explosion beendet das Leben des Gegners. Nun soll die Explosion in 10 Frames an der Stelle, wo der Treffer war ablaufen. Der Rest des Spieles soll aber weiterlaufen.

B.
Der Spieler springt über ein Hindernis. Dabei soll der höchste Punkt nicht auf ein Mal erreicht werden, sondern es soll eine Art Flug zu sehen sein.

C.
Der User soll eine Text-Eingabe machen können, während das Spiel im Hintergrund weiterläuft.

D.
Alle 30 Sekunden soll ein neuer Gegner geboren werden.

E.
Eine Stoppuhr wird in der Bildschirmecke angezeigt.

F.
Die Spielfiguren sollen unabhängig von der Rechnerleistung auf jedem Computer gleich schnell laufen.

Auf all diese Probleme ist das Arbeiten mit Timern die Lösung.

Außerdem möchte ich kurz klären, wann man den CreateTimer() nutzt und wann man besser mit Millisecs() arbeitet.
  • Zuletzt bearbeitet von Midimaster am Mi, Apr 07, 2010 10:17, insgesamt einmal bearbeitet

Midimaster

Betreff: Lektion II: CreateTimer() oder Millisecs()?

BeitragMi, Apr 07, 2010 10:11
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion II: CreateTimer() oder Millisecs()?

CreateTimer() und WaitTimer()
Der große Unterschied zwischen Millisecs() und CreateTimer() besteht darin, dass CreateTimer() die Kontrolle an das Betriebssystem abgibt und erst nach einer eingestellten Zeit dem Programm wieder Rechenleistung zur Verfügung stellt.

Aufgrund dieser Tatsache eignet sich der CreateTimer() nicht führ eine Nutzung einzelner Aktionen in deinem Programm, wo im Hintergrund deines Programmes anndere Aktionen weiterlaufen sollen.

Die optimale Verwendung ist das Einsparen von Rechnerleistung, die unnötig vom Spiel gefressen würde. Dies schont die Hardware des Computers. Teste einfach beide Beispiele und beobachte die Systemleistung im Windows Tast Manager (STRG+ALT+ENTF):

ohne CreateTimer:BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
Repeat
Cls
Rect 100,100,200,200
Flip 0
Until KeyHit(1)


mit CreateTimer:BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
MyTimer=CreateTimer(60)
Repeat
Cls
Rect 100,100,200,200
Flip 0
WaitTimer MyTimer
Until KeyHit(1)


Der Wert 60 bedeutet dabei, dass du wünscht, der Timer soll 60x pro Sekunde wieder in dein Programm zurückspringen. Diese 60 nimmt man deshalb so oft, weil dies auch genau der Bildwiederholungsfrequenz deines Monitors entspricht.

Die Variable MyTimer dient dazu, den erstellten Timer genau zu bezeichnen, um ihn später im Zusammenhang mit WaitTimer() wieder ansprechen zu können. Der Inhalt der Variablen MyTimer ist ein "Pointer" und hat nichts mit irgendwelchen Zeitangaben zu tun.

Mit WaitTimer() gibst du dann jedesmal die Kontrolle an das Betriebsystem ab, das nach dem Ablauf einer 1/60-tel Sekunde genau an dieser Stelle in dein Programm zurückkehrt. Durch diesen Befehl wird also erreicht, dass das Bild garantiert höchstens 60x pro Sekunde aufgebaut wird. Öfter würde keinen Sinn machen und nur Energie verschwenden. Wichtig ist in diesem Fall das FLIP 0 zu verwenden, da BlitzBasic leider sonst nach jedem normalen FLIP ebenfalls eine 1/60-tel Sekunde wartet, aber eben nicht effizient über den WaitTimer(), sondern über eine energiefressende eigene Warteschleife.


Millisecs()

Dieser Befehl liefert dir eine Art "Uhrzeit", aber mit 1/1000-Sekunden Genauigkeit. Der Timer ist Bestandteil des Betriebsystem und startet beim Rechnerneustart mit 0. Du kannst mit ihm also immer feststellen, wielange dein Rechner heute schon läuft:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
Timer=CreateTimer(60)
Repeat
Cls
Text 100,100,"computer runs since " + (MilliSecs()/1000) + " sec"
Text 100,150,"computer runs since " + MilliSecs() + " msec"
Flip 0
WaitTimer Timer
Until KeyHit(1)


Der Millisecs()-Timer läßt sich nicht auf 0 zurücksetzen. Aber Du könnstes im Moment des Startens deines Programms den aktuellen Wert ermitteln und den dann später im Programm immer von Millisecs() abziehen. Als Ergebnis erhältst du den Zeitraum, seit Dein Programm läuft:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
Global Timer=CreateTimer(60)
Global StartZeit%=MilliSecs()
Repeat
Cls
Text 100,100,"programm runs since " + (MilliSecs()-StartZeit) + " msec"
Flip 0
WaitTimer Timer
Until KeyHit(1)


Ebenso ist natürlich eine Art CountDown möglich. Dabei wird StartZeit% auf einen Wert in der Zukunft gesetzt:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
Global Timer=CreateTimer(60)
Global StartZeit%=MilliSecs()+10000
Repeat
Cls
Text 100,100,"CountDown: " + ((StartZeit-MilliSecs())/1000) + " sec"
Flip 0
WaitTimer Timer
Until KeyHit(1)


Diese beiden typischen Verwendungen von Millisecs() sind Grundlage aller Arbeiten mit Timern in deinen eigenen Spielen.
  • Zuletzt bearbeitet von Midimaster am Do, Apr 08, 2010 2:16, insgesamt einmal bearbeitet

Midimaster

Betreff: Lektion III: Frames, Stoppuhr, Schilder

BeitragDo, Apr 08, 2010 2:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Lektion III: Frames, Stoppuhr, Schilder.

Du hast ein Sprite, bei dem ab einem bestimmten Moment (z.b. ein Treffer) an dieser Stelle eine Animation (z.B. Expolosion) aublaufen soll. Das erledigst Du mit einer Kombination auf Warten auf Tastendruck und Timer:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
MyTimer=CreateTimer(60)

Sprite=LoadAnimImage("test.png",1,6)
BildNr%=0
AnimationStart=0

Repeat
If KeyHit(2) Then
AnimationStart=MilliSecs()-1
EndIf
If AnimationStart>0 Then
If AnimationStart<MilliSecs() Then
AnimationStart=MilliSecs()+300
BildNr=BildNr+1
If BildNr=7 Then
BildNr=0
AnimationStart=0
EndIf
EndIf
EndIf
Cls
If BildNr>0 Then
DrawImage Sprite, 100,100,BildNr
EndIf
Flip 0
WaitTimer MyTimer
Until KeyHit(1)


Vor der Repeat-Schleife wird eine Bildfolge mit 6 Tiles (je32x3pixel) geladen. Da BildNr% und AnimationStart% jeweils 0 sind, wird bei DrawImage nichts gemalt. Auch die IF-Verzweigung kann deshalb nicht arbeiten.

Durch den Tastendruck wird nun der Timer AnimationStart% gesetzt und damit kann die If-Verzweigung arbeiten. Dort wird nun BildNr% um 1 hochgezählt und der Timer um 300msec weitergerückt. Bei DrawImage wird nun solange Tile 1 gemalt, bis nach 300msec die IF-Verzweigung wieder aktiv werden kann.

Wenn BildNr% den Wert 7 erreicht, wird der Timer AnimationStart% wieder auf 0 gestellt und der Spuk ist vorbei.

Was Du daraus lernen kannst:
Zitat:
1.
Das Zeichnen eines Explosions-Sprite ist immer aktiv in der Hauptschleife. Man sieht nur nichts, solange BildNr auf 0 steht.
2.
Die Explosion wird durch ein Ereignis ausgelöst (Treffer oder Kollision,etc...). Dort musst du nur den Timer aktivieren.
3.
Der Timer wird nun in einer eigenen IF-Verzweigung dafür sorgen, dass die Bildfolge abläuft und er wird sich auch selbst beenden.





Alle 30 sec ein neuer Gegner:

...ist eigentlich jetzt ganz leicht. Der Timer sorgt in seiner IF-Verzweigung dafür, dass ein neuer Gegner erzeugt wird und verstellt sich dann selbst um 30 sec in die Zukunft:

BlitzBasic: [AUSKLAPPEN]
If GegnerTimer<MilliSecs() Then
CreateNewGegner()....
GegnerTimer= MilliSecs() + 30*1000
EndIf


Auch eine Stoppuhr dürfte kein Problem mehr sein:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
MyTimer=CreateTimer(60)

Global StartZeit%, UhrAnzeige$
....
StartZeit=MilliSecs()
Repeat
If KeyHit(2) Then
; die Ziffern-Taste <1> führt zum Reset der Uhr
StartZeit=MilliSecs()
EndIf
If UhrTimer<MilliSecs() Then
locUhrZeit=MilliSecs()-StartZeit
UhrAnzeige=Int(locUhrZeit/1000) + ":" + (locUhrZeit Mod 1000)
UhrTimer= MilliSecs() + 55
EndIf
Cls
Text 100,100,UhrAnzeige
Flip 0
WaitTimer MyTimer
Until KeyHit(1)


Ein Textfeld wird für 3 Sekunden eingeblendet:

BlitzBasic: [AUSKLAPPEN]
Repeat
...
If MouseHit(1) Then
TextFeldTimer=MilliSecs() + 3*1000
EndIf
If TextFeldTimer>MilliSecs() Then
Color 55,55,55
Rect 100,100,400,100
Color 255,255,255
Text 120,120," Da ist die Maus geklickt worden!"
EndIf
....
Until....
  • Zuletzt bearbeitet von Midimaster am Do, Apr 08, 2010 10:20, insgesamt 2-mal bearbeitet

Midimaster

Betreff: mehrere Timer und Flags

BeitragDo, Apr 08, 2010 10:16
Antworten mit Zitat
Benutzer-Profile anzeigen
mehrere Timer und Flags

du kannst so viele Timer mit Millisecs() aktivieren wie du willst. Letztendlich sind es nur Integer-Variablen. So könnte etwa jeder Gegner schon im Typ mehrere Timer erhalten, die dafür sorgen, dass der Gegner nach einer gewissen Zeit etwas ändert.

BlitzBasic: [AUSKLAPPEN]
Type GegnerTyp
Field X%, Y%, RichtungX%, RichtungY%
Field MoveTimer%, ChangeTimer%, SchussTimer%, ...
End Type
....
Repeat
....
For Gegner.GegnerTyp = Each GegnerTyp
If Gegner\MoveTimer < MilliSecs() Then
Gegner\MoveTimer = MilliSecs()+Rand(10,100)
Gegner\X = Gegner\X+Gegner\RichtungX
Gegner\Y = Gegner\Y+Gegner\RichtungY
EndIf
If Gegner\ChangeTimer < MilliSecs() Then
Gegner\ChangeTimer = MilliSecs()+Rand(1,4)*1000
Gegner\RichtungX = Rand(-5,5)
Gegner\RichtungY = Rand(-5,5)
EndIf
....
Next



Ein Texteingabe-Feld, während das Spiel weiterläuft

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
MyTimer=CreateTimer(60)
Global BallX%, BallY%, AddX%, AddY%
Global FensterTimer%, SpielerName$, locName$, BlinkTimer%, BlinkFlag%

AddX=3
AddY=2
BallX=300
BallY=300
Repeat
Cls
Color 255,0,0
Rect 10,10,780,580
Color 0,111,0
Rect 20,20,760,560
Color 255,255,0
Oval BallX,BallY,10,10
Color 255,155,255
Text 550,40, "Spieler: " + Spielername

If (BallX<20) Or (BallX>770) Then
AddX = -AddX
EndIf
If (BallY<20) Or (BallY>570) Then
AddY = -AddY
EndIf
BallX = BallX+AddX
BallY = BallY+AddY

EingabeFenster

Flip 0
WaitTimer MyTimer
Until KeyHit(1)



Function EingabeFenster()

End Function



Zunächst nehmen wir dieses harmlose Ballspiel als Hintergrund. Die Funktion EingabeFenster soll dann ein Fenster öffnen, um dem User die Möglichkeit der Namenseingabe zu bieten.

Wir erstellen diese Funktion nun in mehreren Schritten:

Im ersten Schritt aktiviert ein Tastendruck das Fenster. Ein Timer sorgt dafür, dass es nun 3 Sekunden lang angezeigt wird:
BlitzBasic: [AUSKLAPPEN]
Function EingabeFenster()
Z= GetKey()
If Z>0 Then
FensterTimer=MilliSecs()+3000
EndIf
If FensterTimer>MilliSecs() Then
Color 55,55,55
Rect 100,200,500,100
Color 99,99,99
Rect 105,205,490,90
Color 255,255,255
Text 120,220, locName
EndIf
End Function


Im zweiten Schritt sorgt die Tatsache, dass der Timer entweder noch nicht gestellt wurde, bzw abgelaufen ist dafür, dass der aktuelle Spielername in das Fenster übernommen wird:

BlitzBasic: [AUSKLAPPEN]
Function EingabeFenster()
Z= GetKey()
If Z>0 Then
If FensterTimer<MilliSecs() Then
locName=SpielerName
Else

EndIf
FensterTimer=MilliSecs()+3000
EndIf
If FensterTimer>MilliSecs() Then
Color 55,55,55
Rect 100,200,500,100
Color 99,99,99
Rect 105,205,490,90
Color 255,255,255
Text 120,220, locName
Else
SpielerName=locName
EndIf
End Function


Im dritten Schritt wird nun der lokale Name aus den getippten Tasten zusammengesetzt. Die erste Taste (mit der das Fenster aktiviert wurde) wird ignoriert:
BlitzBasic: [AUSKLAPPEN]
Function EingabeFenster()
Z= GetKey()
If Z>0 Then
If FensterTimer<MilliSecs() Then
locName=SpielerName
Else
If Z>63 And Z<124 Then
locName=locName +Chr(z)
EndIf
EndIf
FensterTimer=MilliSecs()+3000
EndIf
If FensterTimer>MilliSecs() Then
Color 55,55,55
Rect 100,200,500,100
Color 99,99,99
Rect 105,205,490,90
Color 255,255,255
Text 120,220, locName
; hier kommt gleich ein blinkender Cursor rein
Else
SpielerName=locName
EndIf
End Function


Nun kommt noch ein blinkender Cursor dazu. Die Variable BlinkTimer% sorgt dafür, dass der Strich mal gezeichnet wird, mal nicht. Die Variable BlinkFlag% merkt sich welcher Zustand aktuelle vorliegt. Durch die Werte 300 und 700 wird erreicht, dass die "nicht sichtbare" Phase des Cursors nicht zu lange wird.
BlitzBasic: [AUSKLAPPEN]
	    ....
Text 120,220, locName
If BlinkFlag=1 Then
Rect 120+ StringWidth(locName),220,1,15
EndIf
If BlinkTimer<MilliSecs()
If BlinkFlag=1 Then
BlinkFlag=0
BlinkTimer=MilliSecs()+300
Else
BlinkFlag=1
BlinkTimer=MilliSecs()+700
EndIf
EndIf
....



Auch eine Möglichkeiten des Löschens mit BACKSPACE lässt sich einfügen:
BlitzBasic: [AUSKLAPPEN]
            ....
If Z>63 And Z<124 Then
locName=locName +Chr(z)
ElseIf Z=8 Then
If Len(locname)>0 Then
locName=Left(locName,Len(locName)-1)
EndIf
ElseIf Z=13 Then
FensterTimer=MilliSecs()
Return
EndIf
....

...sowie eine Möglichkeit des schnellen Beendens des Fensters mit der <RETURN>-Taste. Wieder wird einfach der Timer so gestellt, als wären die 3 Sekunden bereits abgelaufen. Ein Timer kann also jederzeit durch andere Aktionen manipuliert werden.


Das ganze Programm:

BlitzBasic: [AUSKLAPPEN]
Graphics 800,600
SetBuffer BackBuffer()
MyTimer=CreateTimer(60)
Global BallX%, BallY%, AddX%, AddY%
Global FensterTimer%, SpielerName$, locName$, BlinkTimer%, BlinkFlag%

AddX=3
AddY=2
BallX=300
BallY=300
Repeat
Cls
Color 255,0,0
Rect 10,10,780,580
Color 0,111,0
Rect 20,20,760,560
Color 255,255,0
Oval BallX,BallY,10,10
Color 255,155,255
Text 550,40, "Spieler: " + Spielername


If (BallX<20) Or (BallX>770) Then
AddX = -AddX
EndIf
If (BallY<20) Or (BallY>570) Then
AddY = -AddY
EndIf
BallX = BallX+AddX
BallY = BallY+AddY

EingabeFenster

Flip 0
WaitTimer MyTimer
Until KeyHit(1)



Function EingabeFenster()
Z= GetKey()
If Z>0 Then
If FensterTimer<MilliSecs() Then
locName=SpielerName
Else
If Z>63 And Z<124 Then
locName=locName +Chr(z)
ElseIf Z=8 Then
If Len(locname)>0 Then
locName=Left(locName,Len(locName)-1)
EndIf
ElseIf Z=13 Then
FensterTimer=MilliSecs()
Return
EndIf
EndIf
FensterTimer=MilliSecs()+3000
EndIf
If FensterTimer>MilliSecs() Then
Color 55,55,55
Rect 100,200,500,100
Color 99,99,99
Rect 105,205,490,90
Color 255,255,255
Text 120,220, locName
If BlinkFlag=1 Then
Rect 120+ StringWidth(locName),220,1,15
EndIf
If BlinkTimer<MilliSecs()
If BlinkFlag=1 Then
BlinkFlag=0
BlinkTimer=MilliSecs()+300
Else
BlinkFlag=1
BlinkTimer=MilliSecs()+700
EndIf
EndIf
Else
SpielerName=locName
EndIf
End Function

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group