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 :allright:

...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! :D

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:

Bild

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