| Weitere Artikel aus dem Elo-Magazin |
Autorennen auf der Pong Platine / Realisierung in BASCOM
von Piotr Platek
Meine Lieblings-Computerspiele sind "Autorennen". Als ich vergangenes Jahr kurz vor Weihnachten die Pong-Platine entdeckt habe, wollte ich als erstes ein Autorennenspiel für die Platine entwickeln. Das habe ich dann, einfach auch zum Kennenlernen der Pong-Platine und der Programmiersprache BASCOM, angefangen umzusetzen und möchte nun meine Erfahrungen und das Ergebnis mit den ELO-Lesern teilen.
Ganzen Artikel lesen...

Die Pong-Platine soll, zumindest in symbolischer Form, eine Fahrbahn mit einem Auto darauf darstellen. Die Fahrbahn wird von oben nach unten "gescrollt", wobei der Streckenverlauf zufällig sein soll. Die Aufgabe des Spielers ist es nun, auf der Fahrbahn zu bleiben, die Begrenzungslinien nicht zu überschreiten und dabei so schnell wie möglich zu fahren. Sieger ist, wer die Gesamtstrecke am schnellsten zurücklegt. Für die Steuerung des Autos werden die beiden Potentiometer der Pong-Platine verwendet, wobei der linke die Geschwindigkeit bestimmt, mit die Fahrbahn nach unten "scrollt", und der rechte das Auto waagerecht hin und her verschiebt (links/rechts).
Als "Retro-Spiel" sollte es idealerweise auch einen der Geschwindigkeit entsprechenden "Retro-Motorengeräusch" erzeugen. Genaugenommen haben wir bis heute noch keine einzige Pong-Anwendung mit einem Ton gesehen bzw. gehört. Hier kann uns ein Piezo-Summer (Piezokeramischer Schallwandler - Conrad Artikel-Nr.: 751669-62) weiterhelfen. Für die Tonerzeugung wird eine Rechteckewelle genutzt, d.h. die entsprechenden „0" und „1"-Werte werden direkt an den Piezo-Summer geschickt. Die Frequenz der Rechteckwelle bestimmt die Tonfrequenz und damit die Tonhöhe. Der hohe Eigenwiderstand des Summers erlaubt es ihn direkt an den Ausgangs-Pin (Ausgang des µ-Controllers) anzuschließen.
Die Programmiersprache BASCOM selbst bietet einen Befehl ("SOUND") zur Tongenerierung an, der aber die Programmausführung derart beeinflußt, dass es zu stockenden Bewegungen der Fahrbahn kommt. Somit ist der Befehl hier ungeeignet. Die Tonerzeugung muss im Hintergrund unabhängig von der Hauptprogrammschleife erfolgen. Das Word „Hintergrund" führt uns quasi automatisch hin zu einer "Interrupt"-gesteuerten Lösung, bei der wir den "Timer 1" verwenden. In der Routine der Interrupt-Verarbeitung des Timers 1 wird der Status des Ausgangs-Pin "PD0" im Zyklus 1-0-1-0 usw. verändert.
Bei den meisten Autorennensspielen sind die Strecken fest und wiederholen sich. Mir erscheint aber eine Fahrt "ins Blaue" interessanter, d.h. der Streckenverlauf sollte sich möglichst zufällig ändern und damit immer wieder anders aussehen. Dazu müssen aber einige Bedingungen erfüllt sein. Zum einen darf sich der Verlauf der Strecke nicht zu abrupt ändern. Zum anderen muß das Programm immer eine "passierbare" Strecke errechnen. Um beides kümmert sich die Unterroutine „Road", die in einer Schleife wiederkehrend eine Zufallszahl erzeugt. Abhängig davon wird die Fahrbahn nach links oder rechts verschoben oder unverändert belassen.
Die Fahrbahnbreite ist in der Konstante „Droadmin" festgelegt. Über die konstante Fahrbahnbreite ist die Passierbarkeit der Strecke sichergestellt. Zur Berechnung des weiteren Fahrbahnverlaufs werden auch der vorletzte und vorvorletzte Positionswert herangezogen. Dadurch ändert sich die Strecke fließend und bildet keinen sägezahnförmigen Verlauf. Die Gesamtlänge der Strecke ist in der Konstante CONST Maxdistance festgelegt.
Eine weitere Bedingung ist die hinreichende Qualität der generierten Zufallszahlen hinsichtlich ihrer "Zufälligkeit". Die verwendete BASCOM-Funktion „Rnd" ("random") liefert nur eine unechte Zufallszahlenfolge, da es sich auch hier um einen deterministischen Generator handelt, der bei gleichen Ausgangsbedingungen immer gleiche Werte liefert. Um zumindest einen nicht-deterministischen Ausgangswert für die Zufallszahlenfolge zu erhalten, wird in diesem Spiel die Potentiometerstellung als externer Anfangswert genutzt.
Code:
Dim ___rseed As Word
...
___rseed = Getadc(7)
Während des Spiels, wird die Variable (Totaltime im Hintergrund /Interrupt-Verarbeitung des Timers 2) ca. alle 0,256 Sekunden inkrementiert, zusätzlich wird die gleiche Variable bei jedem „Unfall" um 100 hochgesetzt. Nach Erreichen des Ziels wird die abgelaufene Zeit als eine Zahl dargestellt, die als ein Vielfaches von 0,256 Sekunden zu interpretieren ist. Der Wert kann als Zahl oder als laufender Text dargestellt werden. Dafür haben wir für die Pong-Platine genügend Beispiele. Hier wollte ich das Spielergebnis aber mal anders darstellen und etwas neues ausprobieren. Die Anzahl der Einer, Zehner, Hunderter usw. (des Zahlenwerts) werden spaltengerecht von rechts nach links als leuchtende LEDs angezeigt, so z.B. die Zahl: 5138760
Download: BASCOM-Quelltext und Hexfile
Das Atorennen in Aktion: www.youtube.com/watch?v=w4RmkVb12u0
Literatur: Lernpaket Mikrocontroller-Technik mit Bascom
$crystal = 8000000
$regfile = "m8def.dat"
$hwstack = 64
$swstack = 64
$framesize = 64
Dim Leds(12) As Word
Dim X As Byte
Dim Y As Byte
Dim I As Byte
Dim N As Word
Dim U As Word
Dim T As Word
Dim Droad As Byte
Dim Proad As Byte
Dim P1 As Byte
Dim P2 As Byte
Dim P3 As Byte
Const Max_pos_car = 4
Dim Car_pos(max_pos_car) As Byte
Dim Car_pos_i As Byte
Dim Ccar_pos As Byte
Dim Pcar_pos As Byte
Dim Accident As Word
Dim Scrolldelay As Word
Dim Update_road As Byte
Dim Pulses As Word
Dim Periods As Word
Dim Soundon As Byte
Dim Totaltime As Long
Dim Bar As Word
Buzzer Alias Portd.0
Dim ___rseed As Word
Const Droadmax = 12
Const Droadmin = 6
Const Car1 = &B1000000000
Const Car2 = &B1100000000
Const Car3 = &B1000000000
Const Ncar1 = &B0111111111
Const Ncar2 = &B0011111111
Const Ncar3 = &B0111111111
'max distance / max times to scroll down
Const Maxdistance = 200
Declare Sub Standby
Declare Sub Explosion
Declare Sub Initialisierung
Declare Sub Road
Declare Sub Collision
Declare Sub Scroll
Declare Sub Results
Declare Sub Clsleds
Initialisierung
Do
Clsleds
' Initialze random generator based on the "random" poti position
___rseed = Getadc(7)
Proad = 1
Droad = Droadmin
P1 = 0
P2 = 0
P3 = 0
Scrolldelay = 100
Totaltime = 0
Accident = 0
Pcar_pos = 1
Ccar_pos = 1
Car_pos_i = 1
Update_road = 0
Totaltime = 0
N = 1
Soundon = 1
While N < Maxdistance
' Speed of the car (moving road under the car :-)) Left Poti
T = Getadc(6)
' Position of the car left/right - Right Poti
U = Getadc(7)
' scale to range of 1-9 (9=12-3) 3 is the width of the car
U = 1023 - U
U = U / 110
' Calculate car position take the max value from the last max readings
Car_pos(car_pos_i) = U + 1
Car_pos_i = Car_pos_i + 1
If Car_pos_i > Max_pos_car Then
Ccar_pos = 0
For Car_pos_i = 1 To Max_pos_car
If Car_pos(car_pos_i) > Ccar_pos Then Ccar_pos = Car_pos(car_pos_i)
Next
Car_pos_i = 1
End If
If Scrolldelay = 0 Then Update_road = 1
Rem Remove car from the board before drawing a new board
Leds(pcar_pos) = Leds(pcar_pos) And Ncar1
Leds(pcar_pos + 1) = Leds(pcar_pos + 1) And Ncar2
Leds(pcar_pos + 2) = Leds(pcar_pos + 2) And Ncar3
Rem scroll according to the "speed" left poti
If Update_road = 1 Then Scroll
Collision
Rem draw the car
Leds(ccar_pos) = Leds(ccar_pos) Or Car1
Leds(ccar_pos + 1) = Leds(ccar_pos + 1) Or Car2
Leds(ccar_pos + 2) = Leds(ccar_pos + 2) Or Car3
Pcar_pos = Ccar_pos
Rem draw a new raw of the road according to the "speed" left poti
If Update_road = 1 Then
Road
Update_road = 0
Scrolldelay = T * 2
' another "mile" was done
N = N + 1
End If
Rem correct the sound in realation to the speed
Pulses = 2000 + T
' Timer1 = Pulses
Waitms 5
Wend
Soundon = 0
Clsleds
Results
Standby
Loop
Dim Tmptotaltime As Long
Dim Ty As Long
Sub Results
Tmptotaltime = Totaltime
' Tmptotaltime = 123456789
For I = 12 To 1 Step -1
If Tmptotaltime > 0 Then
Ty = Tmptotaltime Mod 10
If Ty > 0 Then Leds(i) = 1
For X = 2 To Ty
Leds(i) = Leds(i) * 2
Leds(i) = Leds(i) + 1
Waitms 100
Next
Tmptotaltime = Tmptotaltime \ 10
End If
Next I
Wait 10
End Sub
Sub Scroll
Rem Scroll the board one raw down
For I = 1 To 12
Leds(i) = Leds(i) * 2
Leds(i) = Leds(i) And &B1111111111
Next I
End Sub
Sub Collision
Dim Road1 As Word
Dim Road2 As Word
Dim Road3 As Word
Road1 = Leds(ccar_pos)
Road2 = Leds(ccar_pos + 1)
Road3 = Leds(ccar_pos + 2)
Accident = Road1 And Car1
If Accident = 0 Then
Accident = Road2 And Car2
If Accident = 0 Then
Accident = Road3 And Car3
End If
End If
If Accident <> 0 Then
Explosion
Proad = 1
Droad = Droadmin
P1 = 0
P2 = 0
P3 = 0
Ccar_pos = 5
' collision -> more time
Totaltime = Totaltime + 100
End If
End Sub
Sub Road
Dim Troad As Byte
X = Rnd(13)
If X > 7 Then
Troad = Proad + 1
If Troad <> P3 Then
If Troad <> P2 Then
Troad = Troad + Droad
If Troad <= Droadmax Then
Proad = Proad + 1
End If
End If
End If
Elseif X < 6 Then
Troad = Proad - 1
If Troad <> P3 Then
If Troad <> P2 Then
If Troad > 0 Then
Proad = Proad - 1
End If
End If
End If
End If
P3 = P2
P2 = P1
P1 = Proad
Leds(proad) = Leds(proad) Or &B1
Troad = Proad + Droad
Leds(troad) = Leds(troad) Or &B1
End Sub
'****************** Service-Unterprogramme *********************
Sub Initialisierung
Disable Interrupts
Config Portc = 15 'PORTC als AD-Eingang
Config Portb = Output
Config Portd = 255
Config Timer1 = Timer , Prescale = 1
On Timer1 Tim1_isr
Enable Timer1
Start Timer1
Soundon = 0
Pulses = 1000
Config Timer2 = Timer , Prescale = 8
On Ovf2 Tim2_isr
Enable Timer2
Start Timer2
Config Adc = Single , Prescaler = 64 , Reference = Off
Start Adc
Config Int0 = Low Level 'Falling
On Int0 Int_isr
Enable Interrupts
End Sub
Sub Clsleds
' clear screen
For X = 1 To 12
Leds(x) = 0
Next X
End Sub
Sub Standby
Stop Timer1
Stop Timer2
Portc = 0
Portd = 0
Portb = 0
Stop Adc
Ddrd.2 = 0
Portd.2 = 1
Enable Int0
Powerdown
Ddrd.2 = 1
Portd.2 = 0
Start Adc
Start Timer2
Start Timer1
End Sub
Dim Vhelp As Word
Sub Explosion
For X = 1 To 12
Leds(x) = 1023
Next X
Waitms 10
For X = 1 To 12
For Y = 1 To 12
Periods = X * 10
Periods = Periods * Y
Pulses = 1000 + Periods
Leds(y) = 0
Next Y
Leds(x) = 1023
Waitms 25
Next X
For Y = 1 To 10
For X = 1 To 12
Periods = Y * 10
Periods = Periods * X
Pulses = 500 + Periods
Vhelp = Y - 1
Vhelp = 2 ^ Vhelp
Leds(x) = Vhelp
Next X
Waitms 25
Next Y
For X = 1 To 12
Leds(x) = 0
Next X
End Sub
Int_isr:
Disable Int0
Return
Dim Buz As Bit
Tim1_isr:
Timer1 = 65535 - Pulses
If Soundon = 1 Then
Buz = Buzzer Xor 1
Buzzer = Buz
Else
Buzzer = 0
End If
Return
'******************************* Interrupt - Display ****************************
Dim Vy As Byte
Dim Col As Byte
Dim Portdout As Byte
Dim Portcout As Byte
Tim2_isr:
If Scrolldelay >= 1 Then Scrolldelay = Scrolldelay - 1
Col = Col + 1
If Col = 13 Then Col = 1
Vy = Col + 0
Portd = 0
Portb = 0
Portc = 0
If Col = 1 Then Portb.4 = 0 Else Portb.4 = 1
Portb.3 = 1
Portb.3 = 0
Portb.2 = 1
Portb.2 = 0
Portdout = Low(leds(vy))
Portcout = Portdout And 15
Portdout = Portdout And 240
Portd = Portdout
Portc = Portcout
Portb = High(leds(vy))
Totaltime = Totaltime + 1
Return
End




















