Seite 1 von 5
Automatische Bewegung von 2D Spielfiguren
Verfasst: 13.02.2007 20:09
von Vermilion
Ja hallo erstmal, ich weiß ja nicht, ob ihr es schon wusstet, aber ich arbeite wieder an einem Spiel. Nun aber nicht mehr an dem was ich als "Frozen Heart" ankündigte, da ich 3D Programmierung unter PureBasic für "hoffnungslos" erklärte, sondern an einem 2D Spiel, wo ich auch Sprite3D ausnutzen kann etc.
So.
Nun ist es so, ich habe eine Spielfigur, die soll sich bewegen können. Aber nicht auf Kommando, also nicht wenn man eine Taste drückt, sondern sie hat eine bestimmt Position und eine bestimmte Zielposition(/Koordinate). Und auf diese bestimmte Zielposition soll sie sich nun direkt zu bewegen, in einem bestimmten Tempo. Die Zielposition unterscheidet sich von der Position der Einheit in der X- und Y-Achse.
Oder anders gesagt; kann mir jemand erklären, wie Stargate es in Dune mit seinen Fahrzeugen bzw. <IchkannmirseinenNamenniemerken> (Thomas?) es in Real FARM geschafft hat?
Verfasst: 13.02.2007 20:19
von Kaeru Gaman
für luftlinien-wege ist das supereinfach,
für wege mit hindernissen brauchst du einen PathFind-algo
auf jeden fall läuft es darauf hinaus, dass du einen weg vorberechnest,
und für deine spielfigur schritte pro zeiteinheit angibst.
bei luftline bewegst du halt deine figur in jedem step um einen teilschritt der verbindung.
also z.b., wenn dein zielpunkt 200/300 entfernt ist, bewegst du die figur um jeweils 2/3,
und bist nach 100 steps angekommen.
Verfasst: 13.02.2007 20:53
von Vermilion
Jo, Luftlinie will ich haben, das ist auch bis jetzt die Grundidee des Spiels (Flugzeuge).
Mh, ich schlug mich schon mit der Meshbewegung auf der X- Y-Achse in einer Map rum und hab es nie gebacken gekriegt.
Damit ich das richtig geschnallt habe:
Die X und Y Differenz immer um die Schrittzahl teilen um einen Schritt/Bewegung zu erhalten. Oder?
Edit:
Wie ermittele ich denn den Winkel, der zur aktuellen Sicht der Figur (nehmen wir mal nun 0°, einfach entlang der X-Achse) und der Zielkoordinate?
Verfasst: 14.02.2007 11:27
von Kaeru Gaman
schau dir maln bissel trigonometrie in der Wikipedia an, und den Pythagoras.
der tangens ist definiert als gegenkathete durch ankathete.
für deine figur wäre also Y durch X = tangens zielwinkel,
also zielwinkel = arcustangens von Y / X
das liefert dir nen winkel zwischen 0° und 90°,
in welchem viertelkreis du dich befindest ermittelst du durch die vorzeichen.
(X und Y sind hier abstand zum zielpunkt, is klar)
der luftlinienabstand ist wurzel aus ( X² + Y² ),
das ist der Pythagoras...
wenn du vorab den winkel hast,
dann ist x-abstand = Sinus vom luftlinienabstand,
und y-abstand = Cosinus vom luftlinienabstand.
Verfasst: 14.02.2007 12:45
von STARGÅTE
Hier hast du ein Beispiel von mir :
Code: Alles auswählen
InitSprite()
InitKeyboard()
InitMouse()
OpenScreen(1024, 768, 32, "Bot 1.0")
b.f = 3.14159265/180
Bot_x.f = 512
Bot_y.f = 386
Bot_v.f = 0
Bot_w.f = 0
Bot_dw.f = 0
a = 1
max_v = 50
Repeat
ExamineMouse()
MouseX = MouseX()
MouseY = MouseY()
FlipBuffers()
ClearScreen(0,0,0)
StartDrawing(ScreenOutput())
Bot_x = Bot_x + Cos(Bot_w*b)*Bot_v*0.1
Bot_y = Bot_y + Sin(Bot_w*b)*Bot_v*0.1
Bot_w = Bot_w + Bot_dw
While Bot_w < 0 : Bot_w + 360 : Wend
While Bot_w >= 360 : Bot_w - 360 : Wend
deX = MouseX-Bot_x
deY = MouseY-Bot_y
If deX = 0 :
If deY < 0 : Winkel = -90 : EndIf
If deY >= 0 : Winkel = 90 : EndIf
Else
Winkel = ATan(deY/deX)/b
If deX < 0 : Winkel + 180 : EndIf
EndIf
Unterschied = Winkel-Bot_w
If Unterschied > 180 : Unterschied = Unterschied - 360 : EndIf
If Unterschied < -180 : Unterschied = Unterschied + 360 : EndIf
If Unterschied > 0 And Unterschied <= 180 : Bot_dw = Unterschied/10 : EndIf
If Unterschied < 0 And Unterschied >= -180 : Bot_dw = Unterschied/10 : EndIf
If Bot_dw > 45 : Bot_dw = 45 : ElseIf Bot_dw < -45 : Bot_dw = -45 : EndIf
If Bot_v > Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a) : If Bot_v > 0 : Bot_v = Bot_v - a : EndIf : EndIf
If Bot_v < Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a) : If Bot_v < max_v : Bot_v = Bot_v + a : EndIf : EndIf
;Bot
Circle(Bot_x, Bot_y, 10, RGB(255,255,255))
LineXY(Bot_x, Bot_y, Bot_x+Cos(Bot_w*b)*20, Bot_y+Sin(Bot_w*b)*20)
;Maus
Circle(MouseX, MouseY, 5, RGB(128,128,128))
StopDrawing()
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Escape) <> 0
End
!Erneuert!
Das Ergibnis des ganzen wird sein :
Der BOT dreht sich elegant richtung Ziel (Die Maus) und beschleunigt sobalt er die ungefaire Richtung hat.
Wenn er nahe dran ist bremst er wieder ab.
Verfasst: 14.02.2007 14:47
von Kaeru Gaman
coole demo
...für 4.0x muss nur bei ClearScreen von drei auf ein argument geändert werden....
aber sag mal.. er bremst bei mir irgendwie nicht rechtzeitig,
der fährt immer noch drüber weg und macht nen schlenker...
ist das absicht oder nur n side-effekt von quick'n'dirty?
Verfasst: 14.02.2007 15:01
von STARGÅTE
Das rechtzeitige abbremsen kann man beeinflussen:
An der stelle :
Code: Alles auswählen
...
If Bot_v > Sqr(Pow(deX,2)+Pow(deY,2))/2 : If Bot_v > 0 : Bot_v = Bot_v - 1 : EndIf : EndIf
If Bot_v < Sqr(Pow(deX,2)+Pow(deY,2))/2 : If Bot_v < 50 : Bot_v = Bot_v + 1 : EndIf : EndIf
...
muss man ändern zu
Code: Alles auswählen
...
If Bot_v > (Sqr(Pow(deX,2)+Pow(deY,2))-Wert)/2 : If Bot_v > 0 : Bot_v = Bot_v - 1 : EndIf : EndIf
If Bot_v < (Sqr(Pow(deX,2)+Pow(deY,2))-Wert)/2 : If Bot_v < 50 : Bot_v = Bot_v + 1 : EndIf : EndIf
...
Dabei müsste "Wert" zwischen 0 und 50 sein, muss man selber austesten.
Damit "verschiebe ich das Ziel" und der Bot bleibt früher stehn
oder mit der echten Beschleunigungsformel:
Code: Alles auswählen
...
If Bot_v > Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a) : If Bot_v > 0 : Bot_v = Bot_v - a : EndIf : EndIf
If Bot_v < Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a) : If Bot_v < 50 : Bot_v = Bot_v + a : EndIf : EndIf
...
Dabei ist dann a der Beschleunigungswert (bei mir 1)
PS: Ich habe es oben geändert

Verfasst: 14.02.2007 20:32
von Vermilion
Auha, Trigonometrie machen wir gerade erst in der Schule...
Wusste ich doch, dass das mit Tangens und so weiter kommt... -.-
1000 Dank, für den Code, Stargate!

Verfasst: 16.02.2007 19:23
von Vermilion
Moin. Ich habe mal mit dem Code ein bisschen rumhantiert. Aber nur ein bisschen, hatte nicht viel Zeit in der Woche.
Nun habe ich eine Frage:
Das Sprite wackelt ziemlich stark kurz bevor es sein Zeil erreicht hat. Wie kriege ich hin, dass dies nicht geschieht?
Der Code sieht jetzt so aus:
Code: Alles auswählen
UsePNGImageDecoder()
If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0 Or InitSprite3D() = 0 Or OpenScreen(1280, 1024, 32, "Bot 1.0") = 0
MessageRequester("Error", "Konnte nicht initialisieren.", 0)
EndIf
If CreateSprite3D(0, LoadSprite(#PB_Any, "f15.png", #PB_Sprite_AlphaBlending | #PB_Sprite_Texture)) = 0
CloseScreen()
MessageRequester("Error", "Konnte Sprite nicht laden.", 0)
End
EndIf
b.f = 3.14159265/180
Bot_x.f = 640.0
Bot_y.f = 512.0
Bot_v.f = 0.0
Bot_w.f = 0.0
Bot_dw.f = 0.0
DestinationX = Bot_x
DestinationY = Bot_y
a.f = 0.01
max_v.f = 0.01
Sprite3DQuality(1)
Repeat
ExamineKeyboard()
If ExamineMouse() = 0
CloseScreen()
MessageRequester("Error", "Konnte Mausstatus nicht aktualisieren.", 0)
End
EndIf
MouseX = MouseX()
MouseY = MouseY()
ClearScreen(0)
Bot_x = Bot_x+Cos(Bot_w*b)*Bot_v*0.1
Bot_y = Bot_y+Sin(Bot_w*b)*Bot_v*0.1
Bot_w = Bot_w + Bot_dw
While Bot_w < 0
Bot_w + 360
Wend
While Bot_w >= 360
Bot_w - 360
Wend
deX = DestinationX-Bot_x
deY = DestinationY-Bot_y
If deX = 0
If deY < 0
Winkel = -90
EndIf
If deY >= 0
Winkel = 90
EndIf
Else
Winkel = ATan(deY/deX)/b
If deX < 0
Winkel + 180
EndIf
EndIf
Unterschied = Winkel-Bot_w
If Unterschied > 180
Unterschied = Unterschied - 360
EndIf
If Unterschied < -180
Unterschied = Unterschied + 360
EndIf
If Unterschied > 0 And Unterschied <= 180
Bot_dw = 2+Unterschied/10
EndIf
If Unterschied < 0 And Unterschied >= -180
Bot_dw = -2+Unterschied/10
EndIf
If Bot_dw > 45
Bot_dw = 45
ElseIf Bot_dw < -45
Bot_dw = -45
EndIf
If Bot_v > Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a)
If Bot_v > 0
Bot_v = Bot_v - a
EndIf
EndIf
If Bot_v < Sqr((Sqr(Pow(deX,2)+Pow(deY,2)))*2*a)
If Bot_v < 50
Bot_v = Bot_v + a
EndIf
EndIf
If StartDrawing(ScreenOutput()) <> 0
If MouseButton(1)
Brightness = 255
DestinationX = MouseX
DestinationY = MouseY
Else
Brightness = 128
EndIf
Circle(MouseX, MouseY, 5, RGB(Brightness, Brightness, Brightness))
StopDrawing()
Else
CloseScreen()
MessageRequester("Error", "StartDrawing() = 0", 0)
End
EndIf
If Start3D() <> 0
RotateSprite3D(0, Winkel+90, 0)
DisplaySprite3D(0, Bot_x-64, Bot_y-64, 255)
Stop3D()
Else
CloseScreen()
MessageRequester("Error", "Start3D() = 0", 0)
End
EndIf
FlipBuffers()
If KeyboardPushed(#PB_Key_Escape)
CloseScreen()
End
EndIf
ForEver
Sprite ist hier:

Verfasst: 16.02.2007 20:44
von Scarabol
Sorry konnte deinen Code leider nicht direkt verbessern, aber ich hab mich vor ein paar Tagen auch mit dem Thema beschäftigt und hab folgendes zu bieten. Die F15 folgt der Maus solange man klickt und alles läuft sauber ohne ruckeln oder so, solltest den Code nur noch anpassen, dann müsste alles passen. Allerdings kann ich nicht mit so einem Beschleunigen Bremsen Kram auftrumpfen. So hier der Code:
Code: Alles auswählen
UsePNGImageDecoder()
InitSprite()
InitSprite3D()
InitMouse()
InitKeyboard()
Procedure.f Dif(PosX1, PosY1, PosX2, PosY2)
ProcedureReturn Sqr(Pow(PosX1-PosX2, 2)+Pow(PosY1-PosY2, 2))
EndProcedure
Procedure.f GSin(winkel.f)
ProcedureReturn Sin(winkel*#PI/180)
EndProcedure
Procedure.f GCos(winkel.f)
ProcedureReturn Cos(winkel*#PI/180)
EndProcedure
Procedure.f AngleXY(x1.f, y1.f, x2.f, y2.f)
a.f = x2-x1
b.f = y2-y1
c.f = Sqr(Pow(a,2)+Pow(b,2))
winkel.f = ACos(a/c)*180/#PI
If y2 < y1 : winkel=360-winkel : EndIf
ProcedureReturn winkel+90
EndProcedure
#ScreenWidth = 1024
#ScreenHeight = #ScreenWidth/1.33333333
#Key_Quit = #PB_Key_Escape
OpenScreen(#ScreenWidth, #ScreenHeight, 16, "")
LoadSprite(1, "f15.png", #PB_Sprite_Texture)
CreateSprite3D(1, 1)
PosX = 120
PosY = 120
Speed = 10
TargetX = 200
TargetY = 300
Repeat
FlipBuffers()
ClearScreen(0)
ExamineKeyboard()
ExamineMouse()
If MouseButton(1)
TargetX = MouseX()
TargetY = MouseY()
EndIf
If Dif(TargetX, TargetY, PosX, PosY) >= Speed
Angle = AngleXY(PosX, PosY, TargetX, TargetY)
PosX+GSin(Angle)*Speed
PosY-GCos(Angle)*Speed
EndIf
RotateSprite3D(1, Angle, 0)
Start3D()
DisplaySprite3D(1, PosX-SpriteWidth(1)/2, PosY-SpriteHeight(1)/2)
Stop3D()
StartDrawing(ScreenOutput())
DrawingMode(1)
DrawText(10,10,StrF(Angle), #White)
DrawText(10,60,Str(PosX), #White)
DrawText(10,80,Str(PosY), #White)
Plot(MouseX(),MouseY(),#Red)
StopDrawing()
Until KeyboardReleased(#Key_Quit)
End