BPS #6: Synchrone Bewegung - Auswertung

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

Xeres

Moderator

Betreff: BPS #6: Synchrone Bewegung - Auswertung

BeitragMi, Apr 20, 2011 17:30
Antworten mit Zitat
Benutzer-Profile anzeigen
Wieder ist eine Runde um!

Das war die Aufgabe

Postet hier eure Ergebnisse, Codes, Gedanken. Lernt von den anderen, seht euch deren Quelltext an und versucht euren eigenen zu verbessern.

Diskussion
Postet zu euren Codes stets eine kurze Erklärung mit euren Gedanken in denen ihr simpel gesagt die Frage "Wieso habe ich XY auf diese Art gelöst?" beantwortet. Beiträge, die nur den Code enthalten werden wir aus dem Thread entfernen.

Nächste Aufgabe
In drei Tagen, am 23. April wird die Musterlösung nach editiert und die nächste Aufgabe eingestellt.

Viel Spaß & viel Erfolg!

Musterlösung:
BlitzMax: [AUSKLAPPEN]
SuperStrict									' empfohlen

Framework BRL.Max2D ' optional
Import BRL.Timer
Import BRL.Random
Import BRL.GLMax2D

Graphics 320,240,0 ' Grafikfenster (inkl Doublebuffering)
SeedRnd MilliSecs() ' Zufallszahlen streuen
Global timer:TTimer = CreateTimer(50), ticks:Int ' Timer, synchrone Referenz

' Ball
Global balls:TBall[] = New TBall[0]
Type TBall
Field x:Double, y:Double ' Position
Field sx:Double, sy:Double ' Geschwindigkeit
Field r:Byte, g:Byte, b:Byte ' Farbe
End Type
Function CreateBall()
Local i:Int = balls.length
balls = balls[..i+1]
balls[i] = New TBall
balls[i].x = Rand(10,310) ' X-Position 10 .. 310
balls[i].y = Rand(10,230) ' Y-Position 10 .. 230
balls[i].sx = Rnd(-1,1) ' X-Geschwindigkeit -1 .. 1
balls[i].sy = Rnd(-1,1) ' Y-Geschwindigkeit -1 .. 1
balls[i].r = Rnd(0,$FF) ' Farbe, völlig zufällig
balls[i].g = Rnd(0,$FF)
balls[i].b = Rnd(0,$FF)
End Function
Function UpdateBalls()
' bälle bewegen
For Local i:Int = 0 Until balls.length
balls[i].x = balls[i].x +balls[i].sx
balls[i].y = balls[i].y +balls[i].sy
If balls[i].x<10 ' Bandenkollision, X
balls[i].sx = -balls[i].sx
balls[i].x = 10
ElseIf balls[i].x>310
balls[i].sx = -balls[i].sx
balls[i].x = 310
EndIf
If balls[i].y<10 ' Bandenkollision, Y
balls[i].sy = -balls[i].sy
balls[i].y = 10
ElseIf balls[i].y>230
balls[i].sy = -balls[i].sy
balls[i].y = 230
EndIf
Next
' kollisionen prüfen
For Local i:Int = 0 Until balls.length ' Doppelt verschachtelte Schleife
For Local j:Int = 0 Until balls.length
If i = j Then Exit ' selbstprüfung verhindern
;'distanz berechnen
Local dx:Double = balls[j].x -balls[i].x
Local dy:Double = balls[j].y -balls[i].y
If (dx*dx +dy*dy)<400 ' Distanzprüfung, optimiert
Local tsx:Double = balls[j].sx ' Tauschen der Geschwindigkeiten
Local tsy:Double = balls[j].sy
balls[j].sx = balls[i].sx
balls[j].sy = balls[i].sy
balls[i].sx = tsx
balls[i].sy = tsy
EndIf
Next
Next
End Function
Function DrawBalls()
For Local i:Int = 0 Until balls.length
SetColor balls[i].r,balls[i].g,balls[i].b ' individuelle Farbe pro Ball
DrawOval balls[i].x-10, balls[i].y-10, 20,20 ' 20x20Px Oval
Next
End Function

For Local i:Int = 0 To 9
CreateBall() ' 10 Bälle erstellen
Next

While Not (KeyDown(KEY_ESCAPE) Or AppTerminate()) ' Hauptschleife, per ESC beendbar, AppTerminate ist optional
Cls
For Local i:Int = 1 To ticks ' synchrone Bewegung
UpdateBalls()
Next
DrawBalls()
Flip 0 ' low-CPU Ausgabe
ticks = WaitTimer(timer) ' FPS-Begrenzung
Wend
End
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)
  • Zuletzt bearbeitet von Xeres am Sa, Apr 23, 2011 19:26, insgesamt einmal bearbeitet

ToeB

BeitragMi, Apr 20, 2011 17:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Hier mein Beitrag für Bmax Very Happy :
BlitzMax: [AUSKLAPPEN]
SuperStrict 
Rem
---------------------------------------------------
Synchrone Bewegung
von ToeB
---------------------------------------------------
EndRem


'Neues System erstellen (Grafische Oberfläche)
Local Sys:TSystem = (New TSystem).Create( 320, 240, 50 )

'Dem System die 10 Zufälligen Bälle hinzufügen
SeedRnd( MilliSecs( ) )
For Local i:Int = 1 To 10
Local tmp:TBall = TBall.Create( Sys, Rand( 10, Sys.gfx_w-10 ), Rand( 10, Sys.gfx_h-10 ), 10 ) '{Ball hinzufügen}
tmp.Color( Rand( 32, 255 ), Rand( 32, 255 ), Rand( 32, 255 ) ) '{Zufällige Farbe setzen}
tmp.SetVel( Rnd( -1.0, 1.0 ), Rnd( -1.0, 1.0 ) ) '{Zufällige Geschwindigkeit setzen - Vel = "Velocity"}
Next

'Das System "Ausführen"
Sys.Update( )

'Das System beenden
Sys.Destroy( )
End

Type TSystem
Field gfx_w:Int, gfx_h:Int 'Fenstergröße
Field timer_freq:Int, timer_handle:TTimer 'Timer für Framenunabhängigkeit
Field fps:Int, fps_ms:Int, fps_count:Int

'Methode zum erstellen des Systems mit {fenster_breite, fenster_höhe, timer_frequenz}
Method Create:TSystem( _gfx_w:Int, _gfx_h:Int, _timer_freq:Int=50 )
gfx_w = _gfx_w
gfx_h = _gfx_h
timer_freq = _timer_freq
Graphics( gfx_w, gfx_h, 0, timer_freq )
timer_handle = CreateTimer( timer_freq )
Return Self
EndMethod

'Beendet das Fenster des Systems
Method Destroy( )
EndGraphics( )
EndMethod

'Der Hauptteil des Systems, die Schleife
Method Update( )
Local frames:Int, i:Int
Repeat
'hier werden die von Waittimer herausgefundenen veräumten Frames nachgerechnet
For i = 1 To frames
'Alle Entitys durchgehen und die Methoden zum Updaten, Kollidieren und Malen aufrufen
For Local tmp:TEntity = EachIn TEntity.List
tmp.Update( )
tmp.Collision( )
tmp.Draw( )
Next
Next
'Fps anzeigen
_FPS( )
'Flip ohne VSync (Vertical Synchronisation), ist schneller und erlaubt
'eine Framenbegrenzung mit Waittimer
Flip 0
'Den Timer "ausführen" und veräumte Frames speichern für den nächsten durchgang
frames = WaitTimer( timer_handle )
Cls
Until KeyHit( KEY_ESCAPE ) Or AppTerminate( )
EndMethod

'Zum updaten und anzeigen der FPS (Frames-Per-Second)
Method _FPS( )
If fps_ms <= MilliSecs( ) Then
fps = fps_count
fps_count = 0
fps_ms = MilliSecs( ) + 999
EndIf
fps_count = fps_count + 1
SetColor( 255, 255, 255 )
DrawText( "Fps "+fps, 0, 0 )
EndMethod
End Type

'Entity, Überklasse
Type TEntity
Global List:TList = CreateList( ) 'Liste aller Entitys {TEntity.List}

Field inList:TLink 'Link für den Listeintrag des Objekts
Field system:TSystem 'Zu welchem System das Entity gehört
Field pos_x:Float, pos_y:Float, v_x:Float, v_y:Float 'Positions und Geschwindigkeits Daten
Field col_r:Int, col_g:Int, col_b:Int 'Farbdaten

'Beim erstellen eines Entitys soll es in die Liste {TEntity.List} eingetragen werden
'und eine Standartfarbe soll gesetz werden {weiß}.
Method New( ) Final
inList = List.AddLast( Self )
col_r = 255 ; col_g = 255 ; col_b = 255
EndMethod

'Hier soll das Entity aus der Liste rausgeworfen werden, damit es nicht mehr geupdatet wird
'und dass keine Variable mehr auf das Objekt zeigt sodass der GC (GabarageCollector) ihn löschen kann
'(aus dem Speicher)
Method Delete( ) Final
inList.Remove( )
EndMethod

'Setzt dem Entity eine Farbe
Method Color( _col_r:Int, _col_g:Int, _col_b:Int )
col_r = _col_r
col_g = _col_g
col_b = _col_b
EndMethod

'Setzt dem Entity eine Beschleunigung
Method SetVel( _v_x:Float, _v_y:Float )
v_x = _v_x
v_y = _v_y
EndMethod

'Methoden die durch eine Erweiternde Klasse definiert werden MÜSSEN,
'da sie auf jeden Fall in der System.Update Methode aufgerufen werden
Method Update( ) Abstract
Method Draw( ) Abstract
Method Collision( ) Abstract

End Type

'Erweiternde Klasse von TEntity
'Besitzt also alle Methoden und Fields der TEntity-Klasse,
'muss allerdings die Methoden Update, Draw und Collision
'noch definieren
Type TBall Extends TEntity
Field radius:Float 'radius des Balls

'Einen ball erstellen {Aufruf -> var:TBall = TBall.Create( [...] )}
Function Create:TBall( _system:TSystem, _pos_x:Float, _pos_y:Float, _radius:Float )
Local tmp:TBall = New TBall
tmp.pos_x = _pos_x
tmp.pos_y = _pos_y
tmp.radius = _radius
tmp.system = _system
Return tmp
EndFunction

'Den einen Ball updaten, also Geschwindigkeiten der Position addieren
'und den Ball am Bildschirmrand Abprallen lassen
Method Update( )
pos_x :+ v_x
pos_y :+ v_y
If pos_x+radius > system.gfx_w Then v_x = -v_x; pos_x = system.gfx_w-radius
If pos_x-radius < 0 Then v_x = -v_x; pos_x = radius
If pos_y+radius > system.gfx_h Then v_y = -v_y; pos_y = system.gfx_h-radius
If pos_y-radius < 0 Then v_y = -v_y; pos_y = radius
EndMethod

'Den ball an der Position mit dem Radius und der gewählten Farbe malen
Method Draw( )
SetRotation( 0 ) ; SetBlend( SOLIDBLEND ) ; SetAlpha( 0 ) ; SetScale( 1, 1 )
SetColor( col_r, col_g, col_b )
DrawOval( pos_x-radius, pos_y-radius, radius*2, radius*2 )
SetColor( 255, 255, 255 )
End Method

'Updatet die Kollision für den Ball
Method Collision( )
Local tmpLink:TLink = inList.NextLink( ) 'Der Link zu der EntityListe welcher nach
'dem Objekt selber kommt. Damit muss er
'zum einen nicht sich selber überpürfen und
'zum anderen nicht die die sich vorher in der
'Liste befinden, da diese ja schon mit dem hier
'geprüft wurden (braucht so weniger rechenzeit
'da sachen nicht Doppelt geprüft werden müssen)
While tmpLink <> Null 'Die Liste solange durchgehen bis das Ende erreicht ist (Link = Null)
Local tmp:TBall= TBall( tmpLink.Value( ) ) 'Das TBall-Objekt zu dem aktuellen Link herausfinden
'Distanz ausrechen und prüfen ob die Bälle sich berühren
Local dist_x:Float = tmp.pos_x - pos_x
Local dist_y:Float = tmp.pos_y - pos_y
Local dist:Float = Sqr( dist_x*dist_x + dist_y*dist_y )
Local rad:Float = tmp.radius + Self.radius
If dist <= rad Then
'Geschwindigkeiten Tauschen
Local tmp_v_x:Float = v_x, tmp_v_y:Float = v_y
v_x = tmp.v_x ; v_y = tmp.v_y
tmp.v_x = tmp_v_x ; tmp.v_y = tmp_v_y
'den anderen Ball soweit zurücksetzten dass er den Ball nicht mehr brührt
Local ang:Float = ATan2( dist_y, dist_x )
tmp.pos_x = pos_x + Cos( ang ) * rad
tmp.pos_y = pos_y + Sin( ang ) * rad
EndIf
tmpLink = tmpLink.NextLink( ) 'Den Link nach dem Aktuellen auswählen
Wend
EndMethod
End Type


Sollte genügent auskommentiert sein, sonst einfach fragen Wink

mfg ToeB
Religiöse Kriege sind Streitigkeiten erwachsener Männer darum, wer den besten imaginären Freund hat.
Race-Project - Das Rennspiel der etwas anderen Art
SimpleUDP3.0 - Neuste Version der Netzwerk-Bibliothek
Vielen Dank an dieser Stelle nochmal an Pummelie, welcher mir einen Teil seines VServers für das Betreiben meines Masterservers zur verfügung stellt!

BlitzMoritz

BeitragMi, Apr 20, 2011 18:54
Antworten mit Zitat
Benutzer-Profile anzeigen
BlitzMax: [AUSKLAPPEN]

'BlitzMoritz' BPS#6

SuperStrict
SeedRnd MilliSecs()
AppTitle = "Monty Pythons Flying Circles"
Graphics 320,240
Local timer:TTimer = CreateTimer(50) 'Gemaess Aufgabe 50 Mal pro Sekunde

'===============================================================

Global FlyingCircle:TFlyingCircle[10] 'Statt 10 teste man doch auch einmal 100 Stueck, dann
'wird man sehen, dass sich auch diese nach einiger Zeit
'voneinander geloest haben und akkurat kollidieren.

CreateCircles() 'Alle Kreise erstellen und zufaellig positionieren

'===============================================================

'Die Klasse der sich bewegenden Kreise:

Type TFlyingCircle

Field CenterX#, CenterY# 'der Mittelpunkt des Kreises
Field StepX#, StepY# 'die Bewegungsschritte pro Update
Field RGB%[3] 'die Rot-Gruen-Blau-Farbwerte

'Der Konstruktor erstellt ein neues Objekt aus festgelegten
'Positionswerten und zufaelliger Farbe und Bewegungswerten:
Function Create:TFlyingCircle()
Local NewFlyingCircle:TFlyingCircle = New TFlyingCircle
'Wegen der "intelligenten" ControlCollisions-Function spielt es keine Rolle,
'ob mehrere Kreise anfangs aufeinander liegen: ControlCollisions() wuerde
'dafuer sorgen, dass sie sich sofort moeglichst voneinander entfernen,
'darum sei die Platzierung am Anfang voellig beliebig:
NewFlyingCircle.CenterX = Rand(0,GraphicsWidth())
NewFlyingCircle.CenterY = Rand(0,GraphicsHeight())
NewFlyingCircle.StepX = -1.0 + 2.0 * Rnd()
NewFlyingCircle.StepY = -1.0 + 2.0 * Rnd()
NewFlyingCircle.RGB = [Rand(64,255), Rand(64,255), Rand(64,255)]
Return NewFlyingCircle
End Function

'Das Voranschreiten der Bewegung:
Method update()
CenterX:+StepX
CenterY:+StepY
End Method

'Und (unbedingt getrennt von der update-Methode!) das Zeichnen:
Method draw()
SetColor RGB[0], RGB[1], RGB[2]
DrawOval CenterX-10, CenterY-10, 20, 20
End Method

End Type
'===============================================================

'Einige (nicht interessante) Funktionen, um die Kreise zu verwalten:
Function CreateCircles()
For Local fc% = 0 Until Len(FlyingCircle)
FlyingCircle[fc] = TFlyingCircle.Create()
Next
End Function

Function UpdateCircles()
For Local fc% = 0 Until Len(FlyingCircle)
FlyingCircle[fc].update()
Next
End Function

Function DrawCircles()
For Local fc% = 0 Until Len(FlyingCircle)
FlyingCircle[fc].draw()
Next
End Function

'===============================================================

'Die wichtige und relativ aufwendige Funktion, um Kollisionen der Kreise zu testen:

Function ControlCollisions()

'Die folgenden Berechnungen wirken auf den ersten Blick etwas umstaendlich,
'sorgen aber bequemer Weise dafuer, dass man sich anfangs bei der Erstellung
'der Kreise keine Gedanken um ihre Position machen muss: sie koennen beliebig
'uebereinander liegen und werden sich trotzdem schnell voneinander trennen,
'ja es ist dadurch sogar moeglich, das Programm mit "zu vielen" Kreisen (z.B. 100!)
'laufen zu lassen, da auch diese sich nach einigen Sekunden sauber trennen werden.

For Local fc% = 0 Until Len(FlyingCircle)

'Kollisionen mit dem rechten oder linken Rand pruefen:
If FlyingCircle[fc].CenterX <= 11 Then
FlyingCircle[fc].StepX = Abs(FlyingCircle[fc].StepX)
ElseIf FlyingCircle[fc].CenterX >= GraphicsWidth()-11 Then
FlyingCircle[fc].StepX = -Abs(FlyingCircle[fc].StepX)
End If
'Kollisionen mit dem oberen und unteren Rand pruefen:
If FlyingCircle[fc].CenterY <= 11 Then
FlyingCircle[fc].StepY = Abs(FlyingCircle[fc].StepY)
ElseIf FlyingCircle[fc].CenterY >= GraphicsHeight()-11 Then
FlyingCircle[fc].StepY = -Abs(FlyingCircle[fc].StepY)
End If

'Kollision mit allen noch nicht getesteten Kreisen ueberpruefen,
'darum startet die Laufvariable ec bei fc+1 und nicht bei 0:
For Local ec% = fc+1 Until Len(FlyingCircle)
Local QuadratDistance# = (FlyingCircle[fc].CenterX - FlyingCircle[ec].CenterX) * (FlyingCircle[fc].CenterX - FlyingCircle[ec].CenterX) + (FlyingCircle[fc].CenterY - FlyingCircle[ec].CenterY) * (FlyingCircle[fc].CenterY - FlyingCircle[ec].CenterY)
If QuadratDistance <= 400 Then

'Wenn zwei Kreise zu nahe beieinander liegen, testen wir, ob sie noch naeher kaemen,
'wenn sie ihre Bewegungsrichtung beibehielten. Gehen wir also probeweise weiter:
Local Test_Next_X1% = FlyingCircle[fc].CenterX + FlyingCircle[fc].StepX
Local Test_Next_Y1% = FlyingCircle[fc].CenterY + FlyingCircle[fc].StepY
Local Test_Next_X2% = FlyingCircle[ec].CenterX + FlyingCircle[ec].StepX
Local Test_Next_Y2% = FlyingCircle[ec].CenterY + FlyingCircle[ec].StepY

If QuadratDistance > (Test_Next_X1 - Test_Next_X2) * (Test_Next_X1 - Test_Next_X2) + (Test_Next_Y1 - Test_Next_Y2) * (Test_Next_Y1 - Test_Next_Y2) Then
'Nur wenn dies der Fall ist, duerfen und muessen wir die Farbe und die Bewegungsrichtung austauschen:
Local temp_RGB%[] = FlyingCircle[fc].RGB
FlyingCircle[fc].RGB = FlyingCircle[ec].RGB
FlyingCircle[ec].RGB = temp_RGB
Local temp_StepX# = FlyingCircle[fc].StepX
Local temp_StepY# = FlyingCircle[fc].StepY
FlyingCircle[fc].StepX = FlyingCircle[ec].StepX
FlyingCircle[fc].StepY = FlyingCircle[ec].StepY
FlyingCircle[ec].StepX = temp_StepX
FlyingCircle[ec].StepY = temp_StepY
End If

End If
Next
Next

End Function

'===============================================================

'Und zum Schluss die Hauptschleife:

Local MemoTimerTicks% 'Zum Speichern der Anzahl der vergangenen Ticks
While Not KeyDown(KEY_ESCAPE) And Not AppTerminate()
Cls
'Durch den folgenden kleinen Trick wird dafuer gesort, dass auch bei einer
'Unterbrechung des Ablaufs alle notwendigen Updates nachgeholt werden:
Local NewTimerTicks% = TimerTicks(timer)
For Local count% = 1 To NewTimerTicks - MemoTimerTicks '(wieviel Ticks wurden "verpasst"?)
UpdateCircles()
ControlCollisions()
Next
MemoTimerTicks = NewTimerTicks '...sich die neue Anzahl an Ticks merken
DrawCircles()
SetColor 255,255,255
DrawText "Programmfluss anhalten mit Leertaste", 10, 220
While KeyDown(KEY_SPACE) 'zum willkuerlichen Anhalten des Programm-Flusses
Wend 'zwecks Testen der korrekt aktualisierten Positionen
'---------------------------------------------------------------------
Flip
WaitTimer(timer)
Wend

skey-z

BeitragMi, Apr 20, 2011 19:00
Antworten mit Zitat
Benutzer-Profile anzeigen
BlitzMax: [AUSKLAPPEN]

Rem
Projekt: BPS#6
Autor: skey-z

Vorraussetzungen:
- Fenstergröße(320x240)
- 10 Kreise a 20x20px

Stufe 1:
- Kreise werden an zufälliger Postion erstellt
- die Bewegungsgeschwindigkeit für x und y liegt zwischen -1.0 und 1.0
- die Kreise haben verschiedene, zufällige Farben
- die Kreise prallen vom Fensterrahmen ab

Stufe 2:
- Kreise prallen voneinander ab

Stufe 3:
- Bewegungen sind synchronisiert
- Alle Bewegungen werden 50 mal pro Sekunde ausgewführt
- "verpasste" Frames werden nachgeholt

Tipps:
- von Stufe zu Stufe abarbeiten
- Arrays oder Types für die Bälle
- Funktionen zum erstellen, bewegen und kollidieren der Bälle
- Satz des Pythagoras für Kollisionen benutzen
- CreateTimer und WaitTimer, letzteres mit Rückgabewert für die Synchronisation
EndRem


'saubere Programmierung erzwingen
SuperStrict

'Zufallswert setzen
SeedRnd MilliSecs()

'Grafikfenster Eigenschaften
Global gWidth:Int = 320
Global gHeight:Int = 240
Global gFrames:Int = 1000/50
Global gTimer:TTimer = CreateTimer(50)

'Grafikfenster erstellen
Graphics gWidth, gHeight

'10 Bälle erstellen
For Local i:Int = 0 To 9
TBall.Create()
Next

'Prüfen ob die Bälle sich nicht berühren
Local free:Byte = True
Repeat
For Local ball:TBall = EachIn TBall._ballList
If ball._ballCollission Then
free = False
EndIf
Next
Until free = True

'Ticks abfangen
Local lastTicks:Int

'Hauptschleife
While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())

'Timergesteuerte_aktualisierung
Local ticks:Int = WaitTimer(gTimer)

'verpasste aktualisierungen nachholen, wenn der Bildschirm verschoben wird
If (ticks > 1) Then
lastTicks = ticks

For Local i:Int = 0 To lastTicks-1
TBall.Update()
Next
EndIf



'Verarbeitung_______________
TBall.Update()
'___________________________

'BildschirmAusgabe__________

TBall.Draw()

SetColor(255, 255, 255)
SetBlend(ALPHABLEND)
DrawText "Last: " + lastTicks, 5, 5
'___________________________

Flip
Cls
Wend

Type TBall

'Liste
Global _ballList:TList = CreateList()
Field _ballLink:TLink

'Position
Field _ballX:Float
Field _ballY:Float

'Richtung
Field _ballSpeedX:Float
Field _ballSpeedY:Float

'Größe
Field _ballRadius:Int = 10
Field _ballSize:Int = _ballRadius*2

'Farbe
Field _ballColR:Byte
Field _ballColG:Byte
Field _ballColB:Byte

'Kollisionen
Field _ballCollission:Byte
Field _ballCollStart:Int
Field _ballCollStop:Int = 250

'Listeneintrag
Method New()
_ballLink = _ballList.AddLast(Self)
End Method

'Listenaustrag
Method Remove()
_ballLink.Remove()
End Method

'Ball erstellen
Function Create:TBall()

Local ball:TBall = New TBall

'Startposition festlegen
ball._ballX = Rand(ball._ballRadius*2, gWidth-ball._ballRadius*2)
ball._ballY = Rand(ball._ballRadius*2, gHeight-ball._ballRadius*2)

'Bewegungsrichtung festlegen
Local move:Byte
Repeat
ball._ballSpeedX = Rnd(-1.0, 1.0)
ball._ballSpeedY = Rnd(-1.0, 1.0)

If (ball._ballSpeedX < -0.1) Or (ball._ballSpeedX > 0.1) Then
If (ball._ballSpeedY < -0.1) Or (ball._ballSpeedY > 0.1) Then
move = True
EndIf
EndIf
Until move

'Farbe festlegen
Local colMin:Byte = 64
Local colMax:Byte = 255
ball._ballColR = Rand(colMin, colMax)
ball._ballColG = Rand(colMin, colMax)
ball._ballColB = Rand(colMin, colMax)

Return ball

End Function

'Baelle bewegen und auf Kollision prüfen
Function Update:TBall()

For Local ball:TBall = EachIn TBall._ballList

'Beweggung
ball._ballX :+ ball._ballSpeedX
ball._ballY :+ ball._ballSpeedY

'Kollission mit dem Rand
If (ball._ballX < ball._ballRadius) Or (ball._ballX > gWidth-ball._ballRadius) Then
ball._ballSpeedX :* -1
ElseIf (ball._ballY < ball._ballRadius) Or (ball._ballY > gHeight-ball._ballRadius) Then
ball._ballSpeedY :* -1
EndIf

'Kollission mit anderen Baellen
For Local another:TBall = EachIn TBall._ballList

'Den Ball mit sich selber prüfen, ansonsten bleibt alles auf der Stelle, da
'die Position von ein und dem selben Ball immer eine Distanz < Radius ist
If Not(ball = another) Then

If Not(ball._ballCollission) Then

'Distanzberechnung
Local distX:Int = ball._ballX - another._ballX
Local distY:Int = ball._ballY - another._ballY
Local dist:Int = Sqr(distX*distX + distY*distY)

'Kollissionsprüfung
If (dist < ball._ballRadius*2) Or (dist < another._ballRadius*2) Then

'Quelle zwischenspeichern
Local ballSpeedX:Float = ball._ballSpeedX
Local ballSpeedY:Float = ball._ballSpeedY

'Quelle
ball._ballSpeedX = another._ballSpeedX
ball._ballSpeedY = another._ballSpeedY
ball._ballCollission = True
ball._ballCollStart = MilliSecs()


'Ziel
another._ballSpeedX = ballSpeedX
another._ballSpeedY = ballSpeedY
another._ballCollission = True
another._ballCollStart = MilliSecs()

Exit
EndIf
EndIf
EndIf
Next

'Kollisson zurücksetzen
If (MilliSecs() > (ball._ballCollStart+ball._ballCollStop)) Then
ball._ballCollission = False
EndIf
Next
End Function

'Baelle zeichnen
Function Draw:Tball()

For Local ball:TBall = EachIn TBall._ballList
SetColor(ball._ballColR, ball._ballColG, ball._ballColB)
SetAlpha(0.75)
SetBlend(LIGHTBLEND)
DrawOval(ball._ballX - ball._ballRadius, ball._ballY - ball._ballRadius, ball._ballSize, ball._ballSize)

SetAlpha(1)
SetBlend(SOLIDBLEND)
DrawOval(ball._ballX - (ball._ballRadius-1), ball._ballY - (ball._ballRadius-1), ball._ballSize-2, ball._ballSize-2)
Next

End Function

'Baelle löschen
Function DeleteAll:TBall()
For Local ball:TBall = EachIn TBall._ballList
ball.Remove()
Next
End Function

End Type


Ich habe im Moment leider keine Zeit was genaueres dazu zu schreiben, aber die Kommentare sollten ausreichend sein.
Falls irgendetwas unklar ist, werde ich gerne später dazu Stellung nehmen.
Awards:
Coffee's Monatswettbewerb Feb. 08: 1. Platz
BAC#57: 2. Platz
Twitter

blackgecko

BeitragMi, Apr 20, 2011 21:12
Antworten mit Zitat
Benutzer-Profile anzeigen
So, auch ich hab mal was gemacht (grad eben Smile )
Eigentlich hab ich vorgehabt, den optionalen Teil wegzulassen, weil dieser Grafik-Anhalte-Effekt hier in Linux eh nicht auftritt und ich deshalb nicht testen kann, aber BlitzMoritz hat ja dann gezeigt wie es doch geht. Danke für die Idee!
Programm anhalten also mit Leertaste.

BlitzMax: [AUSKLAPPEN]
SuperStrict

Const GRWIDTH:Int = 320
Const GRHEIGHT:Int = 240

Type TCircle
Global circles:TList = New TList 'Liste aller Kreise
Field x:Float 'Position
Field y:Float
Field radius:Int = 10 'Radius = Durchmesser / 2
Field xspeed:Float 'Geschwindigkeit
Field yspeed:Float
Field red:Int 'Farbe
Field green:Int
Field blue:Int
Field checked_collision:TList 'Die Kreise mit denen die Kollision schon geprüft wurde
Method New()
Local error:Int = True
Repeat 'Positioniere den
error = False 'Kreis so
x = Rand(0,GRWIDTH - 2*radius) 'lange bis
y = Rand(0,GRHEIGHT - 2*radius) 'er sich
For Local i:TCircle = EachIn circles 'nicht mehr
If distance(i) <= 2*radius Then 'mit einem
error = True 'anderen
Exit 'überschneidet
EndIf
Next
Until error = False
xspeed = Rnd(-1,1)
yspeed = Rnd(-1,1)
red = Rand(0,255)
green = Rand(0,255)
blue = Rand(0,255)
checked_collision = New TList
circles.addlast(Self)
EndMethod
Method distance:Float(other:TCircle) 'berechnet den Abstand zweier Kreise
Local distance_x:Float = Abs(x - other.x) 'X-Abstand
Local distance_y:Float = Abs(y - other.y) 'Y-Abstand
Return Sqr(distance_x^2 + distance_y^2) 'Pythagoras
EndMethod
Function updateAll() 'alle Kreis updaten
For Local i:TCircle = EachIn circles
i.update()
Next
EndFunction
Function collideAll() 'Kollisionsprüfung aller Kreise
For Local i:TCircle = EachIn circles
i.collision()
Next
EndFunction
Function drawAll() 'alle Kreise zeichnen
For Local i:TCircle = EachIn circles
i.draw()
Next
EndFunction
Method update() 'Die Update-Methode
x :+ xspeed 'Bewegung
y :+ yspeed
If x < 0 Or x > GRWIDTH-2*radius Then xspeed = -xspeed 'Abprallen vom Rand
If y < 0 Or y > GRHEIGHT-2*radius Then yspeed = -yspeed
checked_collision.clear() 'leeren der Kollisionsliste
EndMethod
Method collision()
For Local i:TCircle = EachIn circles
If i = Self Then Continue 'der Kreis soll ja nicht gegen sich selbst geprüft werden
If i.checked_collision.contains(Self) Then Continue 'wenn der andere Kreis schon gegen unseren geprüft wurde ist eine erneute Prüfung unnötig
If distance(i) <= radius*2 Then 'Wenn sie sich zu nahen kommen -> Geschwindigkeitstausch
Local puffer:Float

puffer = xspeed 'Drei-
xspeed = i.xspeed 'ecks-
i.xspeed = puffer 'tausch

puffer = yspeed 'Drei-
yspeed = i.yspeed 'ecks
i.yspeed = puffer 'tausch

checked_collision.addlast(i) 'Hinzufügen zur Kollisionsliste
EndIf
Next
EndMethod
Method draw() 'Malen
SetColor red,green,blue
DrawOval x,y,2*radius,2*radius
EndMethod
EndType


Function updateScreen(steps:Int) 'Updatet den Bildschirm n-mal
For Local i:Int = 1 To steps
TCircle.updateAll()
TCircle.collideAll()
Next
EndFunction


SetGraphicsDriver GLMax2DDriver()
Graphics GRWIDTH,GRHEIGHT,0
SeedRnd MilliSecs()
Local tim:TTimer = CreateTimer(50)


For Local i:Int = 1 To 10 '10 Kreise erstellen
New TCircle
Next


Repeat
Local missed:Int = tim.wait() 'Der Rückgabewert ist die Anzahl der verpassten Frames
Cls
updateScreen(missed) 'update den Bildschirm so oft wie wir es verpasst haben
TCircle.drawAll() 'und zeichne dann noch die Kreise - 1 Mal
Flip

While KeyDown(KEY_SPACE) 'hält das Programm an
Wend

Until KeyHit(KEY_ESCAPE) Or AppTerminate()
So long and thanks for all the fish.
Fedora 17 | Windows 7 || BlitzPlus | BlitzMax
Rechtschreibflame GO!!! Deppenapostroph | SeidSeit | Deppenakzent | DassDas | Deppenleerzeichen | TodTot | enzigste.info - Ja, ich sammel die.

BlitzMoritz

BeitragDo, Apr 21, 2011 10:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr erfreulich, dass es diesmal mehrere schöne Lösungen gibt, Bravo Smile ! Man kann von allen Codes viel lernen.
Beim Durchstöbern sind mir jedoch ein paar Kleinigkeiten aufgefallen.
Am interessantesten ist natürlich das Konzept, wie die Kollisionsfrage gelöst worden ist.
Dabei habe ich mir erlaubt, alle mit dem "Härtetest" von 100 Kreisen zu testen. Twisted Evil
Bei der Distanzberechnung die Wurzel zu verwenden, wie alle drei es tun, ist nicht notwendig. Den absoluten Betrag beim Abstand der einzelnen Koordinaten bei blackgecko schon gar nicht, weil später sowieso quadriert wird:
BlitzMax: [AUSKLAPPEN]
Method distance:Float(other:TCircle) 'berechnet den Abstand zweier Kreise
Local distance_x:Float = Abs(x - other.x) 'X-Abstand
Local distance_y:Float = Abs(y - other.y) 'Y-Abstand
Return Sqr(distance_x^2 + distance_y^2) 'Pythagoras
EndMethod
Bei s-Key erschließt sich mir diese Fallunterscheidung nicht:
BlitzMax: [AUSKLAPPEN]
If (dist < ball._ballRadius*2) Or (dist < another._ballRadius*2) Then
Selbst wenn die Radien verschieden wären (was gemäß Aufgabe nicht der Fall ist), müsste man das doch eher so formulieren:
BlitzMax: [AUSKLAPPEN]
If dist < ball._ballRadius + another._ballRadius Then

blackgeckos Kollisionsansatz ist am einfachsten, bei 100 Kreisen gibt es da leider fast immer welche, die nie mehr voneinander loskommen (ich weiß, 100 Kreise waren nicht verlangt Wink ).
s_Key versucht auf zweierlei Arten zu verhindern, dass Kreise aneinander kleben: Erstens merkt er sich in einer Variablen, ob ein Kreis mit einem anderen bereits kollidiert ist, d.h. er will verhindern, dass genau diese Kollisionsfrage erneut gestellt wird. Allerdings berücksichtigt er dabei die anderen Kreise nicht mehr. Deren Kollisionsabfragen mit diesem einen Kreis finden von nun an nicht mehr statt und sie können sich unkontrolliert nähern, was das Folgende problematisch werden lässt:
Denn er löscht zweitens diesen Speicher nach einer festgelegten Zeit von einer Viertelsekunde, was etwas willkürlich ist. Haben es zwei Kreise innerhalb dieser Zeit nicht geschafft, sich zu lösen, werden sie es von nun an kaum noch tun.
ToeB's Idee, die Kreise bei Kollision sofort um den notwendigen Abstand wegspringen zu lassen, ist da zuverlässiger, allerdings nur wenn genug Platz da ist. Bei 100 Kreisen wird es schon etwas eng und es kann passieren, dass das große Wegspringen sofort wieder eine dichte Kollision mit einem anderen Kreis verursacht. Irgendwann klappt es jedoch und alle 100 Kreise haben sich gelöst. (Auch bei mir dauert es ein paar Sekunden, aber ich glaube, ein bisschen schneller).
Für einen Fehler halte ich es allerdings, wenn er das Zeichnen der Kreise in die Update-Methode steckt, die die Positionen neu berechnet. Ich finde, die hat da nichts zu suchen. Wenn man nämlich dort zwischen seinem "Flip 0" und "Cls" folgende Kontrolle einschiebt und benutzt:
BlitzMax: [AUSKLAPPEN]
If KeyHit(KEY_RETURN) Then Delay 2000
While KeyDown(KEY_SPACE)
Wend
... dann sieht man, dass beim willkürlichen Anhalten des Programms alle fehlenden Kreise "verschmiert" nachgemalt werden, was wohl nicht beabsichtigt ist, weil es nicht schön aussieht und unnötig Rechenzeit kostet.

skey-z

BeitragDo, Apr 21, 2011 14:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Wie ich schon im Aufgabenthread schrieb, war es die erstbeste möglichkeit, die mir eingefallen ist und leider hatte ich danach keine Zeit mehr um mich weiter damit auseinander zu setzen.

Bei 10 Kreisen habe ich es einige Minuten durchlaufen lassen und dabei zugeschaut, wie sich alle Kreise schön voneinander abstoßen. Natürlich könnte es unter "mysteriösen" Umständen vorkommen, dass sich zwei verhaken, aber das hält auch nicht ewig an.
Awards:
Coffee's Monatswettbewerb Feb. 08: 1. Platz
BAC#57: 2. Platz
Twitter

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group