dbLounge
Wie die Zeit vergeht....
1. Allgemein
Ein Verweis auf den TIMER() Befehl.
2. Einfache Anwendungen: Befehlsersatz
Als Ersatz bzw. Erweiterung für bestimmte (Zeit-)Befehle bietet sich der TIMER()-Befehl geradezu an:

WAIT n
SLEEP n

Diese Befehle werden oft benutzt, um eine Warteschleife zu programmieren, z.B. um ein Ladebildschirm anzuzeigen:

LOAD BITMAP "logo.jpg",0
WAIT 5000

Dieses Programm lädt ein Bild, zeigt es an und hält für 5 Sekunden an. Das ist zwar einfach zu programmieren, aber der User muss bei jedes Mal 5 Sekunden warten, ohne dass er es abbrechen kann. Je öfter er das Programm startet, um so mehr wird ihn die Zwangspause nerven.
Folgender Code macht das gleiche, nur der User kann die Wartepause mit der Maus abbrechen:
1
2
3
4
5
6
7
8
9
10
...
Load bitmap "logo.jpg", 0
Zeit = TIMER()

REM warte 5 Sekunden

Repeat
  Sync
Until Zeit + 5000 < TIMER() Or MOUSECLICK()

REM Hier geht es weiter
...
3. (Fast) Perfektes Timing
Ein Problem, was bei nahezu allen Programmen auftritt, ist: Man kann nicht wissen, wie schnell bzw. langsam die Rechner der User sind, auf denen das Programm laufen soll.
Wenn man feste Werte z.B. für die Object- oder Sprite-Bewegung vorgibt, laufen die Bewegungen auf einem schnellen Rechner eventuell zu schnell, oder im umgekehrten Fall auf einen älteren Rechner eben zu langsam ab.
Dafür gibt es mehrere Lösungsmöglichkeiten:
3.1. Feste Syncrate wie z.B. SYNC RATE 60
Die schlechteste Lösung. Auf schnellen Rechner, die die geforderte Syncrate erreichen, ist alles in Ordnung, aber auf älteren Rechnern, die keine 60 Frames schaffen ist die Bewegung zu langsam.
Für kleinere Projekte geeignet, aber man verschenkt Rechenpower, mehr dazu unter Punkt 3.3
3.2. Zeitabhängige Bewegung
Eine beliebte Methode, bei der pro Sekunde eine festgelegte Geschwindigkeit erzielt wird. Zum Beispiel wird pro Sekunde ein Sprite um 50 Pixel bewegt. Damit ist es (fast) egal, ob das Programm auf einem schnellen oder langsamen Rechner läuft. Auf langsamen Rechnern kann allerdings das Problem auftreten, das die Bewegung „sprunghaft“ ausfällt.
Ein Beispielcode spare ich mir, da im englischen Tutorial (Link siehe oben) oder im deutschen Forum die Methode des Öfteren vorgestellt wurde.
3.3. Eigene Syncrate basteln
Schauen wir uns zuerst mal ein Programm an:

(Anmerkung: Bei diesem Programm habe ich bewusst auf den DOT-Befehl verzichtet, damit Grafikkarten als Flaschenhals ausfallen. Hier kommt es nahezu auf CPU-Power an, da in der Hauptschleife 2 mal eine FOR-NEXT-Schleife mit 32768 (also 65536 mal ;) ) Werten durchlaufen wird.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
Rem Project: Timer
Rem Created: 29.06.2005 17:42:19

Rem ***** Main Source File *****


Gosub Init


rem MAINLOOP

Do
  
  
  Gosub set_spots
  Gosub calc_buffer
  Gosub draw_buffer
  
  
  
  Ink Rgb(0,255,0),0
  Text 10,10,"F/s: "+Str$(screen fps())+"  "
  
  Fastsync
  
Loop







REM SUBs



draw_buffer:
  
  
  For x=0 To width-1
    For y=0 To height-1
      
    	
      Rem DOT-Ersatz, damit man direct ins IMAGE schreiben kann
      col=buffer(x,y)
      memPos = (y*width*4)+(x*4)
      Write memblock dword 1,memPos+12,palette(col)
      
    Next y
  Next x
  
  
  Make image from memblock 1,1
  
Return







set_spots:
  
  For x=1 To width-1 Step 2
    
    If Rnd(3)=1 Then buffer(x,height)=255
    
  Next x
  
Return





calc_buffer:
  
  For x=1 To width-1
    For y=height To 1 Step -1
      
      col=buffer(x,y)
      
      If col>2
        buffer(x+Rnd(2)-1,y-1)=col-1
        buffer(x,y)=col-3
      Endif
      
    Next y
  Next x
  
Return






Init:
  
  
  Sync rate 0
  Sync on
  Sync : Sync
  
  Set text font "ARIAL" : Set text size 24
  Set text opaque : Randomize timer()
  rem hide mouse
  
  
  width=256
  height=128
  
  Get image 1,0,0,width,height,0
  Make memblock from image 1,1
  
  
  
  Dim buffer(width,height) As Byte
  Dim palette(255) As Dword
  
  
  
  rem Palette
  
  For i=0 To 85
    palette(i)  =Rgb(Int(255/85*i),0,0)
    palette(i+85) =Rgb(255,Int(255/85*i),i/2)
    palette(i+170) =Rgb(255,255,Int(255/85*i))
  Next i
  
  
  
  
  
  rem Object (man kann auch Sprite verwenden)
  Make object plain 1,120,70
  Texture object 1,1
  Set object texture 1,2,0
  Set object 1,1,0,0,2,0,0,0
  
  
  
  
  Position camera 0,0,-70
  Point camera 0,0,0
  
  Backdrop off
  
Return
Was macht nun das Programm?
Es wird ein Buffer für das Image angelegt (dim buffer(width,height) as byte) und eine Farbpalette (dim palette(255) as dword) mit Farbwerten von weiß über gelb nach rot. In dem Imagebuffer sind (später) Werte von 0-255 eingetragen, die den Index der Farbpalette präsentieren. Zuerst werden im unteren Bereich zufällig „spots“ eingesetzt (set_spots) mit dem Wert 255(=weiß). Dann wird „gescrollt“ (calc_buffer): Man holt den Farbindex aus dem Buffer(x,y), verringert ihn und setzt ihn eine Zeile höher (mit zufälligen x-Versatz) ein.

Zum Schluß wird der Buffer in das Image eingesetzt (draw_buffer).

So, nun zurück zum Thema Timer.
Um mehr Performance herauszuholen, basteln wir uns eine eigene Syncrate.
Einfach die Hauptschleife durch das hier ersetzen:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
rem MAINLOOP

Do
  
  TIME=timer()
  
  If TIME>Delay
    
    Delay=TIME+20
    
    Gosub draw_buffer
    Gosub set_spots
    Gosub calc_buffer
    
  Endif
  
  
  
  
  Ink Rgb(0,255,0),0
  Text 10,10,"F/s: "+Str$(screen fps())+"  "
  
  Fastsync
  
Loop
Was bewirkt die Änderung?
Statt bei jedem syncen das Bild neu aufzubauen, wird es nur noch alle 20 ms aufgebaut (Delay=TIME+20, das entspricht SYNC RATE 50 (1000/20) ), während das Restprogramm außerhalb der IF-Schleife weiterhin mit SYNC RATE 0 weiterläuft! Und genau das ist der gewinnbringende Vorteil gegenüber SYNC RATE 50. Die Animation läuft jetzt zwar langsamer ab (eben wie mit eingestellter SYNC RATE 50), aber mit wesentlich höherer Framerate.
Ruckler braucht man auch auf langsamen Rechnern nicht befürchten. Sie können natürlich auch im Extremfall auftreten, aber man hat mit dieser Methode höhere Leistungsreserven als bei Punkt 3.2. Auch handelt es sich nicht um eine „Warteschleife“, wie schon im Forum vermutet, sondern im Prinzip entspricht diese Methode dem alten ON TIMER GOSUB aus anderen BASIC-Sprachen.
Welche Teile seines Programms man in der TIME-Schleife auslagert (Grafikdarstellund und / oder Berechnungen) ist abhängig von der Aufgabenstellung und welche Routinen den Flaschenhals darstellen. Allgemeingültig kann man das so nicht sagen, aber z.B. eine Scoreanzeige oder eine Kollisionsabfrage muss nicht unbedingt bei jedem Sync durchgeführt werden.
Welche Werte eignen sich für den DELAY-Wert?
Da das Auge bewegte Bilder ab 25 Frames (deshalb auch bei PAL-Fernseher 25 Halbbilder=50 Hz) als „flüssig“ erkennt, ist der Höchstwert von 40 (1000/25) für Grafikdarstellung geeignet. Falls man aber intensive Berechnungen (z.B. für A.I.) anstatt der Grafikdarstellung auslagert, sind auch höhere Werte möglich.
Zu kleine Werte sollte man allerdings auch nicht wählen, denn wenn die Ausführung länger dauert als der DELAY-Wert ist der Effekt gleich null. Für diesen Fall könnte man z.B. die Routinen einmal im „Leerlauf“ vor der Hauptschleife durchlaufen lassen, und die benötigte Zeit mittels TIMER() messen.
Was sollte man noch beachten?
Im FULLSCREEN EXCLUSIVE MODE ist die Methode (wie im alten DBC) relativ sinnlos, da der Bildaufbau mit der Wiederholfrequenz des Monitors synchronisiert wird.
Ach ja, falls es noch nicht aufgefallen ist: SYNC RATE 0 ;)
Viel Erfolg für eure aktuellen und zukünftigen Projekte!

MONOS