Scripte - so oder besser anders?

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen

M0rgenstern

Betreff: Scripte - so oder besser anders?

BeitragDi, Mai 03, 2011 23:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo Leute,
Ich habe eine Frage, die vielleicht ein wenig seltsam anmutet.
Ich bin dabei einen Shooter zu schreiben, mit vertikalem Scrolling. Das ganze soll also ein bisschen Retro sein (ich weiß im Moment leider kein Beispiel).
Jedenfalls kann ich mich entsinnen, dass die Gegner immer irgendwielche Muster geflogen sind.
Da ich aber nicht jedes Muster im Code eigens anlegen wollte, habe ich mir überlegt eine kleine Scriptsprache für die Muster zu schreiben.
Leider habe ich mich damit noch nie beschäftigt und wusste daher anfangs nicht wie ich es machen soll.
Ich habe jetzt mal ein Programm geschrieben, das so funktioniert, wie ich möchte.
Meine Frage ist jetzt, ob die Lösung in Ordnung ist, also ob das allgemein so ungefähr gemacht wird oder ob meine Lösung eher ungünstig ist.
Wäre wirklich super, wenn sich das jemand mal anschauen würde und mir Rückmeldung geben könnte, da ich mir wirklich unsicher bin.
Ich habe meinen Code komplett kommentiert, ihr könnt mich aber auch noch genau fragen, wenn Stellen unklar sein sollten.

Hier ist der Code:
BlitzMax: [AUSKLAPPEN]
Type TEnemy 'Gegnerklasse
Global tlAllEnemies:TList = New TList 'Liste mit allen Gegnern
Field v2dPosition:TVector2D 'Position
Field v2dSpeed:TVector2D 'Allgemeine Geschwindigkeit
Field v2dCurrentSpeed:TVector2D 'Aktuell festgelegte Geschwindigkeit
Field v2dSize:TVector2D 'Größe des Rechtecks
Field tsMovingScript:TScript 'Zugehöriges Script zum Bewegen

Method New()
tlAllEnemies.AddLast(Self) 'In die Liste einfügen
End Method

Function Create:TEnemy() 'Konstruktor
Local Enemy:TEnemy = New TEnemy
'Eigenschaften festlegen
Enemy.v2dPosition = TVector2D.Create(GraphicsWidth() / 2, GraphicsHeight() / 2)
Enemy.v2dSize = TVector2D.Create(20, 20)
Enemy.v2dSpeed = TVector2D.Create(1.5, 1.5)
Enemy.v2dCurrentSpeed = TVector2D.Create(Enemy.v2dSpeed.Get_X(), Enemy.v2dSpeed.Get_Y())

Enemy.tsMovingScript = TScript(TScript.tlAllScripts.First()) 'Testweise das erste Script in der Liste

Return Enemy
End Function

Method Move() 'Für die Bewegung
v2dCurrentSpeed = tsMovingScript.InterpretScript(v2dSpeed) 'Das Script ausführen und entsprechend die Richtung festlegen
v2dPosition = TVector2D.Add(v2dPosition, v2dCurrentSpeed) 'Bewegung: Vektoren addieren
End Method

Method Draw() 'Rechteck zeichnen
DrawRect(v2dPosition.Get_X(), v2dPosition.Get_Y(), v2dSize.Get_X(), v2dSize.Get_Y())
End Method
End Type

Type TFileLine 'Klasse für Zeilen in einer Datei
Global tlAllLines:TList = New TList 'Alle gelesenen Zeilen
Field sLine:String 'Inhalt der Zeile

Method New()
tlAllLines.AddLast(Self) 'In die Liste einfügen
End Method

Function Create:TFileLine(sLine:String) 'Konstruktor
Local Line:TFileLine = New TFileLine
Line.sLine = sLine 'Inhalt festlegen
Return Line
End Function

Function ReadFileOut(sFileName:String) 'Eine komplette Datei auslesen
Local tsFile:TStream

If FileType(sFileName) = 1 Then 'Falls vorhanden...
tsFile = ReadFile(sFileName) '...Datei öffnen
While Not Eof(tsFile) 'Alle Zeilen durchgehen
TFileLine.Create(ReadLine(tsFile)) 'Und eine neue Instanz anlegen
Wend
CloseFile(tsFile) 'Datei schließen
EndIf
End Function

Function PrintAll() 'Alle Zeilen im Debugger ausgeben
For Local Line:TFileLine = EachIn tlAllLines
DebugLog(Line.sLine)
Next
End Function
End Type

Type TScriptLine 'Klasse für die Zeilen im Script
Global tlAllScriptLines:TList = New TList 'Alle geladenen Zeilen
Field sLine:String 'Die komplette Zeile
Field sDirective:String 'Nur die Anweisung (links von ":")
Field iParameter:Int 'Nur der Parameter (rechts von ":")

Method New()
tlAllScriptLines.AddLast(Self) 'In die Liste einfügen
End Method

Function Create:TScriptLine(sLine:String, sDirective:String, iParameter:Int) 'Konstruktor
Local ScriptLine:TScriptLine = New TScriptline
'Eigenschaften festlegen
ScriptLine.iParameter = iParameter
ScriptLine.sDirective = sDirective
ScriptLine.sLine = sLine

Return ScriptLine
End Function

Method PrintLine() 'Die Daten einer Zeile im Debugger ausgeben
DebugLog("Line: " + sLine)
DebugLog("Directive: " + sDirective)
DebugLog("Parameter: " + iParameter)
End Method

Function MakeScriptLines() 'Anhand der ausgelesenen Datei Scriptzeilen erstellen
Local iPos:Int, sRight:String, sLeft:String 'Ein paar Variablen

For Local Line:TFileLine = EachIn TFileLine.tlAllLines 'Für jede gelesene Zeile
If Line.sLine <> "" And Line.sLine <> " " Then 'Wenn es keine leere Zeile ist
If Line.sLine = "/S" Or Line.sLine = "/E" Then 'Wenn die Zeile den Anfang oder das Ende eines Scripts anzeigt
TScriptLine.Create(Line.sLine, Line.sLine, 0) 'Dann gibt es auch keine speziellen Parameter
Else
iPos = Instr(Line.sLine, ":") 'Position des ":"
sLeft = Left(Line.sLine, iPos - 1) 'Die linke Seite, also die Anweisung
sRight = Right(Line.sLine, Len(Line.sLine) - iPos) 'Die rechte Seite, also der Parameter
TScriptLine.Create(Line.sLine, sLeft, Int(sRight)) 'Scriptzeile erstellen
EndIf
EndIf
Next

End Function

Function PrintAll() 'Alle Scriptzeilen ausgeben
Local i:Int = 1
For Local ScriptLine:TScriptLine = EachIn tlAllScriptLines
DebugLog("Line-Nr.: " + i)
ScriptLine.PrintLine()
DebugLog("")
i = i + 1
Next
End Function

End Type

Type TScript 'Klasse für die Scripte
Global tlAllScripts:TList = New TList 'Alle erstellten Scripte
Field tlLines:TList 'Liste aller Scriptzeilen, die zum Script gehören
Field tCurrentLine:TScriptLine 'Momentan bearbeitete Scriptzeile
Field iCurrentParameter:Int 'Parameter, der runtergezählt wird
Field iInterpretingTime:Int 'Zeit nach der das Script weiter verarbeitet werden soll
Field iLastInterpreting:Int 'Zeit der letzten Interpretation

Method New()
tlAllScripts.AddLast(Self) 'In die Liste einfügen
End Method

Function Create:TScript() 'Konstruktor
Local Script:TScript = New TScript

Script.tlLines = New TList 'Liste anlegen
Script.tCurrentLine = Null 'Erstmal keine Zeile, da noch nichts zugeordnet wurde
Script.iCurrentParameter = 0
Script.iInterpretingTime = 10000 '10 Sekunden, damit es nicht zu schnell geht
Script.iLastInterpreting = 0

Return Script
End Function

Function MakeScripts() 'Scripte anhand der geladenen Scriptzeilen erstellen
Local Script:TScript
For Local ScriptLine:TScriptLine = EachIn TScriptLine.tlAllScriptLines 'Alle Zeilen durchgehen
If ScriptLine.sLine = "/S" Then Script = TScript.Create() 'Wenn die Startanweisung gelesen wird, wird ein neues Script erstellt
Script.tlLines.AddLast(ScriptLine) 'Die Zeile zu den Zeilen des Scripts hinzufügen
Script.tCurrentLine = TScriptLine(Script.tlLines.First()) 'Bearbeitete Linie auf die erste Zeile setzen
Next
End Function

Method PrintScriptLines() 'Die Zeilen eines Scripts im Debugger ausgeben
For Local ScriptLine:TScriptLine = EachIn tlLines
ScriptLine.PrintLine() 'Einfach die Methode aufrufen
DebugLog("")
Next
End Method

Method ExecuteScript() 'Das SCript ausführen
Local Link:TLink

If iCurrentParameter <= 1 Then 'Wenn der aktuelle Parameter <= 1 ist wird die nächste Zeile bearbeitet
'Nächste Zeile über die Links finden:
Link = tlLines.FindLink(tCurrentLine)
Link = Link.NextLink()
tCurrentLine = TScriptLine(Link.Value())

If tCurrentLine.sLine = "/E" Then 'Wenn der Inhalt der Zeile die Anweisung fürs Ende des Scripts ist
tCurrentLine = TScriptLine(tlLines.First()) 'Wieder die erste Zeile nehmen
Link = tlLines.FindLink(tCurrentLine)
Link = Link.NextLink()
tCurrentLine = TScriptLine(Link.Value()) 'Und dann auf die Zeile nach der ersten Zeile setzen
EndIf
iCurrentParameter = tCurrentLine.iParameter 'Den Parameter aufs Maximum setzen
Else
iCurrentParameter = iCurrentParameter - 1 'Ansonsten einfach den Parameter runterzählen
EndIf

End Method

Method InterpretScript:TVector2D(v2dVec:TVector2D) 'Die aktuelle Zeile interpretieren
If (MilliSecs() > (iLastInterpreting + iInterpretingTime)) Then 'Wenn die Zeit schon vergangen ist
ExecuteScript() 'Das Script ausführen
Select tCurrentLine.sDirective 'Die momentane Anweisung suchen
Case "F" 'Vorwärts (also nach unten)
'X-Geschwindigkeit auf 0 setzen und Y-Geschwindigkeit auf einen negativen Wert setzen
If v2dVec.Get_Y() < 0 Then
Return TVector2D.Create(0, -v2dVec.Get_Y())
Else
Return TVector2D.Create(0, Abs(v2dVec.Get_Y()))
EndIf
Case "B" 'Rückwärts (also nach oben)
'X-Geschwindigkeit auf 0 setzen und Y-Geschwindigkeit auf einen positiven Wert setzen
If v2dVec.Get_Y() < 0 Then
Return TVector2D.Create(0, v2dVec.Get_Y())
Else
Return TVector2D.Create(0, -v2dVec.Get_Y())
EndIf
Case "L" 'Links
'Y-Geschwindigkeit auf 0 setzen und X-Geschwindigkeit auf einen negativen Wert setzen
If v2dVec.Get_X() < 0 Then
Return TVector2D.Create(v2dVec.Get_X(), 0)
Else
Return TVector2D.Create(-v2dVec.Get_X(), 0)
End If
Case "R" 'Rechts
'Y-Geschwindigkeit auf 0 setzen und X-Geschwindigkeit auf einen positiven Wert setzen
If v2dVec.Get_X() < 0 Then
Return TVector2D.Create(-v2dVec.Get_X(), 0)
Else
Return TVector2D.Create(Abs(v2dVec.Get_X()), 0)
End If
End Select
Return TVector2D.Zero() 'Wenn nichts gefunden wurde zumindest den Nullvektor zurückgeben
iLastInterpreting = MilliSecs() 'Die Zeit wieder neu setzen
EndIf
End Method
End Type


Und so sieht ein Script zum Beispiel aus:
Zitat:
/S
L:20
F:10
B:40
R:30
/E


Lg, M0rgenstern

Xeres

Moderator

BeitragDi, Mai 03, 2011 23:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Genaue Definitionen habe ich nicht im Kopf, aber es sieht mir auf den ersten Blick wie eine Dateiformat mit relativ komplizierter Lade-Routine aus.
Ein Simpel zu lesendes Format (für Mensch & Maschine) wäre z.B. XML, eine echte Skriptsprache ist mit Lua direkt eingebaut.
Mit dieser Lösung sehe ich aber auch kein Problem, ganz ähnlich habe ich auch schon Level gespeichert.

Was du genau verwendest, hängt eigentlich von deinem Geschmack ab - und wie leicht es sich in zukünftigen Projekten wiederverwenden lässt.
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)

BladeRunner

Moderator

BeitragMi, Mai 04, 2011 8:43
Antworten mit Zitat
Benutzer-Profile anzeigen
Du musst Dir nur eine einfache Frage beantworten: erfüllt es seinen Zweck, also deine damit verbundenen Erwartungen? Wenn ja: go for it. Wenn nein: machs neu, Sam!
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Midimaster

BeitragMi, Mai 04, 2011 9:22
Antworten mit Zitat
Benutzer-Profile anzeigen
Ein Problem könnte werden, dass den verschiedenen Mustern eine Art "Unterscheidungsmerkmal" fehlt. So etwas wie eine Name oder eine Überschrift. Das Modell der INI-Dateien sieht hier vor, dass es noch Überschriften gibt:
Zitat:
[Muster 1]
L=20
F=10
B=40
R=30
[Muster 2]
L=20
F=10
B=40
R=30
[Ende]


Somit könnten verschiedenste Gegner die Daten aus der gleichen INI-Datei holen:
Zitat:
[Alien-Muster 1]
L=20
F=10
B=40
R=30
[Alien-Muster 2]
L=20
F=15
B=45
R=22
[Monster-Muster 1]
L=2
F=1
B=4
R=3
[Monster-Muster 2]
L=6
F=5
B=4
R=3
[Ende]

M0rgenstern

BeitragMi, Mai 04, 2011 10:06
Antworten mit Zitat
Benutzer-Profile anzeigen
@BladeRunner: Bisher erfüllt es seinen Zweck, also werd ich es mal so stehen lassen.
@Minimaster:
Es ist auch eigentlich so gedacht, dass alle Muster in einer Datei stehen. Das Programm weiß wo ein SCript aufhört und wo eines anfängt und ich habe mir überlegt die Muster in der Datei nach Schwierigkeitsgraden zu ordnen, so, dass die Muster die als letzte in den Speicher geladen werden auch für die schwersten Gegner gelten, so dass ich immer nur Gegner.Level SChritte in die Lister laufen muss um ein entsprechendes Muster zu finden.
Aber deine Lösung wäre eigentlich besser und relativ schnell eingebaut.

Mein Problem ist im Moment mit der ganzen Sache einfach: Wenn ich die "Sprache" erweitere und Dinge wie Verknüpfungen etc mit reinbringe (also Vorwärts UND Links), dann weiß ich nicht wie vorteilhaft select case in diesem Fall wäre.
Also, ob ich da einfach nen riesiegen Select-Case Block stehen haben soll oder ob man das anders lösen könnte.

Lg, M0rgenstern

BladeRunner

Moderator

BeitragMi, Mai 04, 2011 10:36
Antworten mit Zitat
Benutzer-Profile anzeigen
im Endeffekt ist ein Compiler / Interpreter nichts anderes als ein sich selbst fütternder endlicher Automat ( http://de.wikipedia.org/wiki/Endlicher_Automat ), und genau das kannst Du mit select-case schön realisieren.
Es wird halt je flexibler Du das gestalten willst immer komplexer, aber das liegt in der Natur der Sache.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Midimaster

BeitragMi, Mai 04, 2011 19:25
Antworten mit Zitat
Benutzer-Profile anzeigen
aha, erst jetzt verstehe ich deine Abkürzungen R L B F.

bedeutet...

Zitat:
/S
L:20
F:10
B:40
R:30
/E


..., dass erst 20 schritte nach links, dann 10 nach vorne, dann 40 zurück und dann wieder 30 nach rechts gelaufen werden?

Dann wäre ja auch längere Ketten denkbar, oder, z.b.

Zitat:
/S
L:20
F:10
B:40
F:10
R:30
F:10
/E


Brächte es etwas, das Ganze auch um Diagonalen zu erweitern?

Code: [AUSKLAPPEN]
A   B   C
    |
D - X -  F
    |
G   H    I


Zitat:
/S
H:1
A:1
B:2
I:1
D:5
X:8
/E


So könnten Sie sogar 8 Sekunden stehenbleiben.

Oder du könntest gleich immer pro Buchstabe nur 1 Schritt beschreiben:

"HABBIDDDDDXXXXX"

so erhältst du sehr komplexe Bewegungen.

M0rgenstern

BeitragMi, Mai 04, 2011 19:41
Antworten mit Zitat
Benutzer-Profile anzeigen
Genau Midimaster, das ist der Sinn der Sache.
Ja, es können beliebig lange Ketten gebildet werden.
Und ja, diagonale Bewegung ist nun möglich, da ich einen Befehl, oder besser eine Verknüpfung eingebaut habe.
Die sieht dann so aus: Zitat:
(L AND B):20

Damit würde man nach links oben, also diagonal 20 Ausführungen lang laufen.
Natürlich wird bei der Ausführung des Scripts eine Anweisung übersprungen, die zwei entgegengesetzte Richtungen verknüpft.

Stimmt, einfach stehen zu bleiben habe ich noch gar nicht beachtet, das wäre noch eine interessante Sache.
Die Zahlen sind nicht direkt Schritte, sondern diese Zahlen geben an, wie oft diese Zeile ausgeführt werden soll.

Lg, M0rgenstern

ToeB

BeitragMi, Mai 04, 2011 20:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Aber was ist wenn du sie schöne Formvollendete Kurven fliegen lassen möchtest ? würde ich mir auch noch überlegen, einen "Befehl" dafür zu schreiben, wie
Code: [AUSKLAPPEN]
TURN(-30;0):90


Würde heißen, das der Bezugspunkt bzw. Kreismittelpunkt der Drehung bei X-30 und Y+0 liegt (also relative Angabe). 90 heißt, er soll sich um 90° wenden. Oder so ähnlich. Würde auf jeden Fall ne Menge Script-Code sparen und auch schön ausehen !


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!

M0rgenstern

BeitragDo, Mai 05, 2011 12:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey ToeB:
An Kreise etc habe ich auch schon gedacht. Meine Überlegung ist momentan dazu einfach Sin als Scriptbefehl mit reinzupacken.

Lg, M0rgenstern

kriD

BeitragDo, Mai 05, 2011 17:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich denke, das der Vorschlag von Toeb am besten das beinhaltet, was du suchst.

Du kannst denn eine kurven flieg Funktion, eine gerade bewegungs funktion und was du sonst noch brauchst (teleportieren etc. blabla, da sind dir denn ja keine Grenzen gesetzt) machen.

In der Ini schreibst du dann einfach nur die Funktionen mit den Parametern nacheinander weg und hast dadurch deine zeitliche Abfolge.

lg kriD
Wenn ich du wäre, wäre ich lieber ich!

M0rgenstern

BeitragFr, Mai 06, 2011 15:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey kriD, ich glaube du hast Recht.
Ich werd das ganze wohl ein wenig umschreiben.

Lg, M0rgenstern

M0rgenstern

BeitragFr, Mai 06, 2011 18:50
Antworten mit Zitat
Benutzer-Profile anzeigen
Sorry für Doppelpost, aber es hat sich ein Problem ergeben:
Zitat:
Aber was ist wenn du sie schöne Formvollendete Kurven fliegen lassen möchtest ? würde ich mir auch noch überlegen, einen "Befehl" dafür zu schreiben, wie
Code:
TURN(-30;0):90


Würde heißen, das der Bezugspunkt bzw. Kreismittelpunkt der Drehung bei X-30 und Y+0 liegt (also relative Angabe). 90 heißt, er soll sich um 90° wenden. Oder so ähnlich. Würde auf jeden Fall ne Menge Script-Code sparen und auch schön ausehen !


Könnte mir bitte jemand erklären, wie man sowas in Code umsetzen könnte?
Also, das mit den relativen Angaben und der Kreisbewegung um diese Angaben?
Wäre wirklich super, komme nämlich nicht weiter.

Lg, M0rgenstern

mpmxyz

BeitragFr, Mai 06, 2011 19:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich würde es zunächst einmal so machen:
1. Bestimme die absoluten Koordinaten des Drehpunktes. Je nachdem, wie du es haben möchtest, musst du die relativen Koordinaten um den Ausrichtungswinkel des Objektes drehen und auf die Objektposition hinzuaddieren.
2. Ziehe die absolute Position des Drehpunktes von der Objektposition ab.
3. Drehe diese zum Drehpunkt relative Position mit dem im Script bestimmten Winkel um den (relativen) Ursprung.
4. Addiere die Drehpunktposition wieder auf die Objektposition.

Man kann das Ganze aber noch ein wenig vereinfachen.
Code: [AUSKLAPPEN]
Pabs: absolute Position des Objektes
ROTcur: Rotationsmatrix zum Objekt
Drel: die relative Position des Drehpunktes
Mrot: Rotationsmatrix zur jetzt stattfindenen Drehung des Objektes
PabsNeu: neue Position

1. Dabs=Pabs+ROTcur*Drel
2. Prel=Pabs-Dabs=-ROTcur*Drel '<-Vereinfachung der Berechnung
3. PrelNeu=Mrot*Prel
4. PabsNeu=PrelNeu+Dabs

(Kurzzusammenfassung: Schritt 2 wird zu: "Die Objektposition wird auf die negierte und gedrehte, relative Drehpunktposition gesetzt." <- Großer Name!)

Kannst du mit linearer Algebra umgehen? (Rotationsmatrizen erzeugen, Vektoren und Matrizen multiplizieren und Vektoren addieren)
Ich liefere ansonsten gerne noch etwas Code nach.
mfG
mpmxyz
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer

ToeB

BeitragFr, Mai 06, 2011 19:22
Antworten mit Zitat
Benutzer-Profile anzeigen
Also mit den Bezugs-Punkt musst du vor dem Drehen Abspeichern (Variablen), da sich ja die Position des Gegners beim Drehen verändert. Also So :
Code: [AUSKLAPPEN]
Bezugspunkt_X = GegnerX + Script_Parameter_X
Bezugspunkt_Y = GegnerY + Script_Parameter_Y


Dann rechnest du den Radius und den Winkel dazu aus, geht mit
BlitzBasic: [AUSKLAPPEN]
Winkel = ATan2( Script_Parameter_Y, Script_Parameter_X )
Radius = Sqr( Script_Parameter_X^2 + Script_Parameter_Y^2 ) ;Wobei ich hier die ^2 auschreiben würde, ist einfach schneller


Wenn du das Hast, setzt du eine Turn-Variable auf True, sodass das Programm jetzt weis das der Gegner sich jetzt drehen kann.

Jetzt musst du nur noch pro Schritt (hinter dem ":") einmal den Winkel hochzählen, in dem Fall von 1 bis 90. Dann Positionierst du den Gegner von dem Abgespeicherten Bezugspunkt aus in dem Winkel (Sin ´/ Cos )
BlitzBasic: [AUSKLAPPEN]
GegnerX = BezugspunktX + Cos( Winkel + WinkelSchrittZahl  ) * Radius
GegnerY = BezugspunktY + Sin( Winkel + WinkelSchrittZahl ) * Radius


Damit kannst du Beliebige Drehungen, mit beliebingen Radien und Winkel etc. Problemlos mit dieser kleinen Zeile darstellen.

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!

M0rgenstern

BeitragFr, Mai 06, 2011 22:47
Antworten mit Zitat
Benutzer-Profile anzeigen
@ToeB:
Bei deiner Möglichkeit erhalte ich irgendwie nur eine Bewegung auf einer Geraden.
Wenn ich den Bezugspunkt vor jeder Bewegung festlege ist es eine Gerade, wenn ich den Bezugspunkt ganz am Anfang festlege und dann nie mehr ändere, dann bewegt es sichgar nicht.

@mpmxyz:
Also, lineare Algebra ist bei mir der Oberstufenstoff.
Ich kann mit Vektoren, Ebenen, Geraden umgehen, aber leider nicht mit Matrizen, wobei das hier ja ein zweidimensionales Array sein müste oder?
Kurz: Es wäre wirklich nett, wenn du ein wenig mehr Infos liefern würdest.

ToeB

BeitragFr, Mai 06, 2011 23:24
Antworten mit Zitat
Benutzer-Profile anzeigen
Ja also, du gibst ja z.B. an er soll 90° drehen, also 90 mal 1° drehen.
user posted image

Hier die Position des Gegners (Schwarz), die Relative Position des Bezugspunktes (Rot) und die Absolute Position des Punktes (DunkelRot). Zu diesem Punkt muss jetzt vor dem erstmaligen Bewegen der Winkel und der Abstand ausgerechnet werden, damit dieser Übernommen werden kann.

Dann Musst du nur noch beim Drehen den Gegner im ausgerechneten Abstand un Winkel mit Sin&Cos + den Schritt-Winkel (1-90). Also in dem Fall wäre der winkel 0° (vom Bezugspunkt zum Gegner).

So wie ich es dir geschrieben habe, sollte es klappen sofern du es auch sinngemäß deinem Programm angepasst hast (ist ja nur pseudocode).

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!

mpmxyz

BeitragSa, Mai 07, 2011 13:08
Antworten mit Zitat
Benutzer-Profile anzeigen
(Wenn dich nur die fertige Formel für den um den Uhrsprung gedrehten Vektor interessiert, sieh dir die fette Formel an!)
Zuerst einmal zur Drehmatrix:
Diese ist so aufgebaut:
Zitat:
(Cos(w) -Sin(w))
(Sin(w) Cos(w))

Wenn man diese Drehmatrix mit einem Vektor multipliziert, wird dieser Vektor um den Ursprung gedreht. (um den Winkel w im Uhrzeigersinn)
Der Spezialfall für w=0 ist die so genannte Einheitsmatrix, welche keine Veränderung bewirkt:
Zitat:
(1 0)
(0 1)


Zitat:
(1 0)*(0 1)=(? ?)
(0 2)_(1 2)_(? ?)

Die Multiplikation von Matrizen findet allgemein so statt:
1. Man wählt eine Zeile der ersten Matrix und eine Spalte der zweiten Matrix. Zusammen ergeben diese Positionen den berechneten Eintrag in der neuen Matrix.
Zitat:
(1 0)*(0 1)=(? ?)
(0 2)_(1 2)_(? ?)

2. Nun geht man bei der ersten Matrix Schritt für Schritt jeden Eintrag in der ausgewählten Zeile durch und multipliziert diesen mit dem dazugehörigen Eintrag der ausgewählten Spalte der zweiten Matrix. Die Ergebnisse werden addiert.
Zitat:
(1 0)*(0 1)=(1*0+0*1 ?)
(0 2)_(1 2)_(? ______ ?)

3. So werden auch die anderen Felder der neuen Matrix berechnet.
Zitat:
(1 0)*(0 1)=(1*0+0*1 1*1+0*2)=(0 1)
(0 2)_(1 2)_(0*0+2*1 0*1+2*2)_ (2 4)


In diesem Fall ist der Vektor eine 2x1-Matrix:
Zitat:
(xNeu)=(+Cos(w) -Sin(w))*(x)=(Cos(w)*x-Sin(w)*y)
(yNeu)_(+Sin(w) +Cos(w)) (y)_(Sin(w)*x+Cos(w)*y)


Nun musst du nur noch diese Formel in das Programm einarbeiten:Code: [AUSKLAPPEN]
Pabs: absolute Position des Objektes
ROTcur: Rotationsmatrix zum Objekt
Drel: die relative Position des Drehpunktes
Mrot: Rotationsmatrix zur jetzt stattfindenen Drehung des Objektes
PabsNeu: neue Position

1. Dabs=Pabs+ROTcur*Drel 'Dabs: Absolute Position des Drehpunktes
2. Prel=Pabs-Dabs=-ROTcur*Drel 'Prel: relative Position des Objektes zum Drehpunkt (ohne Drehung)
3. PrelNeu=Mrot*Prel 'PrelNeu: die relative Position des Objektes nach der Drehung
4. PabsNeu=PrelNeu+Dabs

Wenn du den Vektor (ROTcur*Drel) zwischenspeicherst, brauchst du die Matrixmultiplikation dabei nur zweimal anzuwenden.
mfG
mpmxyz
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer

M0rgenstern

BeitragSa, Mai 07, 2011 16:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Hey.
Ich habe jetzt beide Versionen ausprobiert, bzw. versucht. Zu mpmxyz: Sorry, aber ich komm mit der Rechnerei da irgendwie nicht richtig klar. Also damit bin ich nicht weiter gekommen.

Zu ToeB:
Dein Vorschlag hat prinzipiell funktioniert, aber jedesmal wenn er eine neue Kurve begonnen hat, ist er ein paar Pixel gesprungen, was irgendwie sehr seltsam war.
Der Code dazu sieht so aus:
BlitzMax: [AUSKLAPPEN]
				If bTurning Then
Local fAngle:Float = (ATan2(iaparameterlist[1] - v2dReferencePoint.Get_Y(), iaParameterlist[0] - v2dReferencePoint.Get_X()) + 360) Mod 360
Local fRadius:Float = Sqr(iaParameterList[0] * iaParameterList[0] + iaParameterList[1] * iaParameterList[1])
Return TVector2D.Create(v2dReferencePoint.Get_X() + Cos(fAngle + piCounter) * fRadius, v2dReferencePoint.Get_Y() + Sin(fAngle + piCounter) * fRadius)
Else
'(ATan2(Point2.y - Point1.y, Point2.x - Point1.x) + 360)
v2dReferencePoint = TVector2D.Create(pVecPosition.Get_X() + iaParameterList[0], pVecPosition.Get_Y() + iaParameterList[1])
DebugLog("Referenz-X|Y: " + v2dReferencePoint.Get_X() + " | " + v2dReferencePoint.Get_Y())
DebugLog("Vektor-X|Y: " + pVecPosition.Get_X() + " | " + pVecPosition.Get_Y())
bTurning = True
Local fAngle:Float = (ATan2(iaparameterlist[1] - v2dReferencePoint.Get_Y(), iaParameterlist[0] - v2dReferencePoint.Get_X()) + 360) Mod 360
Local fRadius:Float = Sqr(iaParameterList[0] * iaParameterList[0] + iaParameterList[1] * iaParameterList[1])
Return TVector2D.Create(v2dReferencePoint.Get_X() + Cos(fAngle + piCounter) * fRadius, v2dReferencePoint.Get_Y() + Sin(fAngle + piCounter) * fRadius)
EndIf

Es sieht fast aus, als würde er zu dem Bezugspunkt springen und dann erst die Kurve fliegen.
Habe ich vielleicht irgendwas falsch umgesetzt?

Ich habe auch noch etwas anderes ausprobiert, aber da habe ich einfach das Problem, dass ich die Geschwindigkeit viel zu hoch ist. Und je größer der Abstand zum Bezugspunkt ist, umso größer desto größer ist auch die Geschwindigkeit (blöde Bahngeschwindigkeit eben).

Lg, M0rgenstern

mpmxyz

BeitragSa, Mai 07, 2011 17:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe meinen Ansatz nun in Code umgesetzt.
Die Position des Punktes ist hier aber absolut gegeben.
BlitzMax: [AUSKLAPPEN]
SuperStrict
Framework brl.GLMax2D
Import brl.Timer
Import brl.PolledInput
Import brl.KeyCodes

Graphics 800,600
Local timer:TTimer=CreateTimer(60)

Local objX:Float,objY:Float
Local turnX:Float,turnY:Float

Const SPEED:Float=1.0

Repeat
Cls
'Zeichnen
DrawOval objX-5,objY-5,10,10
Plot turnX,turnY
'Positionieren
If MouseHit(MOUSE_LEFT)
objX=MouseX()
objY=MouseY()
EndIf
If MouseHit(MOUSE_RIGHT)
turnX=MouseX()
turnY=MouseY()
EndIf

'Drehen um den Punkt
objX:-turnX
objY:-turnY

Local oldX:Float=objX
Local oldY:Float=objY

Local radius:Float=Sqr(objX*objX+objY*objY) 'für eine gleichmäßige Geschwindigkeit notwendig
Local deltaW:Float=(SPEED/radius)*180.0/Pi 'Berechnung im Bogenmaß: v=r*w -> w=v/r

objX=Cos(deltaW)*oldX-Sin(deltaW)*oldY
objY=Cos(deltaW)*oldY+Sin(deltaW)*oldX

objX:+turnX
objY:+turnY

If KeyDown(KEY_SPACE) Then Delay 500
WaitTimer timer
Flip 0
Until KeyHit(KEY_ESCAPE)

mfG
mpmxyz
Moin Moin!
Projekte: DBPC CodeCruncher Mandelbrot-Renderer

Gehe zu Seite 1, 2  Weiter

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group