Seite 2 von 3

Verfasst: 11.10.2006 23:32
von Froggerprogger
Ahoi Agent.
Hierfür eignen sich parametrisierte Kurven. Das ist nicht Schlimmes, sondern Folgendes:
Du nutzt eine Variable t, die einfacherweise von 0 nach 1 rennt.
Zu jedem 'Zeitpunkt' t dazwischen berechnest du die aktuelle Position (cx, cy) zwischen Start (sx, sy) und Ziel (zx,zy).
Z.B. so:

Code: Alles auswählen

  cx = sx + t * (zx-sx)
  cy = sy + t * (zy-sy)
Setze mal t=0 und t=1 ein, dann siehst du was passiert. Allgemein nennt sich das Geradengleichung in Parameterform.

Je nachdem, ob du dein t nun linear von 0 bis 1 laufen lässt, oder aber anders gestaucht, gestreckt, kannst du hierdurch andere Bewegungsabläufe erzeugen. z.B. durch:

Code: Alles auswählen

t = t*t
wird die Bewegung erst langsam, und dann schneller sein. (Beachte: für t in [0,1] bleibt t^2 in [0,1] !)

Durch die Formel um die cx/cy zu berechnen kannst du außerdem einen anderen Weg, als den direkten nutzen. Z.B. führt folgendes:

Code: Alles auswählen

  cx = sx + Sin(t*#Pi/2) * (zx-sx)
  cy = sy + (1-Cos(t*#Pi/2)) * (zy-sy)
über einen bogenförmigen Weg (setze wieder mal t=0 und t=1 ein, das liefert wieder Start- bzw. Zielpunkt. Dazwischen wird cx über Sinus und cy über 1-Cosinus interpoliert.)

Hier ein Beispiel, das verschiedene Bewegungsabläufe mit den beiden genannten Wegen kombiniert:

Code: Alles auswählen

; 11.10.06 by Froggerprogger
sx.f = 600
sy.f = 600
zx.f = 400
zy.f = 200
t.f = 0

Procedure Display(cx, cy)
  ClearScreen(0)
  StartDrawing(ScreenOutput())
    Circle(cx, cy, 35, RGB(255,0,0))
  StopDrawing()
  FlipBuffers()
EndProcedure

InitSprite()
OpenWindow(0,0,0,600,400,"Bewegungs-Tests")
OpenWindowedScreen(WindowID(0), 0, 0, 1024, 768, 1, 0, 0)

For run=0 To 2
  Display(sx, sy) : Delay(1000)
  For i=0 To 100
    t = i/100.0
    Select run
      Case 0 :
                ; Bewegungsablauf
                ;t = t ; lineare Bewegung
                ;t = Sqr(t) ; erst schnell, dann langsam
                t = t*t ; erst langsam, dann schnell
            
                ; direkter Weg
                cx = sx + t * (zx-sx)
                cy = sy + t * (zy-sy)
      
      Case 1 :  
                ; Bewegungsablauf
                t = 0.5 + Sin(t*#Pi-#Pi/2)/2 ; langsam -> schneller -> langsam
              
                ; direkter Weg
                cx = sx + t * (zx-sx)
                cy = sy + t * (zy-sy)
      
      Case 2 :
                ; Bewegungsablauf      
                t = 0.5 + Sin(t*#Pi-#Pi/2)/2
            
                ; bogenförmiger Weg
                cx = sx + Sin(t*#Pi/2) * (zx-sx)
                cy = sy + (1-Cos(t*#Pi/2)) * (zy-sy)
    EndSelect
 
    Display(cx, cy)
    
    While WindowEvent()
    Wend
    Delay(10)
  Next
  
  Display(zx, zy) : Delay(1000)
Next
Generell kannst du da mit der Funktion für t den Geschwindigkeitsverlauf kontrollieren (Hier eignen sich einfacherweise alle Funktionen, die von [0,1] nach [0,1] abbilden, wie z.B. t^2 oder Sqr(t) oder der modifizierte Sinus)
und mit der Funktion um aus t die cx und cy zu berechnen, den Weg verändern.

Viel Spaß beim rumprobieren!

Verfasst: 12.10.2006 00:20
von PMV
öhm ... ja ... zu spät /:->
egal ... ich hab hier trotzdem nen Code für dich :D

Beschleunigung und Bremsen von Objekten:

Code: Alles auswählen

Structure Object
  x.f
  y.f
  Speed.f          ;aktuelle Geschwindigkeit
  Acceleration.l   ;Beschleunigungskraft
  BrakeForce.l     ;Bremskraft
  Angle.f          ;die zu laufende Richtung (Winkel)
  State.l
EndStructure
Global Object.Object
Object\Acceleration = 10
Object\BrakeForce = 10

Global NewList WayPoint.Point()

Define TimeLoop.f, LoopTimer.l, BrakeWay.f, Way.f



Procedure.f Angle(KatheteX.l, KatheteY.l)
  If KatheteX < 0 And KatheteY <= 0 
    ProcedureReturn ATan(-KatheteY/-KatheteX)*180/#PI+180
  ElseIf KatheteX < 0 And KatheteY >=0 
    ProcedureReturn ATan(-KatheteX/KatheteY)*180/#PI+90
  ElseIf KatheteX > 0 And KatheteY > 0 
    ProcedureReturn ATan(KatheteY/KatheteX)*180/#PI
  ElseIf KatheteX >= 0 And KatheteY<0 
    ProcedureReturn ATan(-KatheteX/KatheteY)*180/#PI+270 
  EndIf  
EndProcedure

Procedure CreateWayPoints(Number.l)
  Protected i.l
  ClearList(WayPoint())
  
  For i = 1 To Number
    AddElement(WayPoint())
    WayPoint()\X = Random(180) + 10
    WayPoint()\Y = Random(80) + 10
  Next
  FirstElement(WayPoint())
  Object\x = WayPoint()\X
  Object\y = WayPoint()\Y
  Object\State = 1
EndProcedure


InitSprite()
OpenWindow(0, 0, 0, 200, 100, "Beschleunigung", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0), 0, 0, 200, 100, 0, 0, 0)
CreateWayPoints(5)

LoopTimer = ElapsedMilliseconds()
Repeat
  Select Object\State
    Case 0 ; Beschleunigung
      Object\Speed + Object\Acceleration * TimeLoop
      BrakeWay = Pow(Object\Speed,2) / 2 / Object\BrakeForce
      Way = Sqr(Pow(WayPoint()\X - Object\X, 2) + Pow(WayPoint()\Y - Object\Y, 2))
      If Way <= BrakeWay
        ;wenn verbleibender Weg kleiner oder gleich des benötigten Bremsweges ist, bremsen!
        Object\State = 1
      EndIf
      
    Case 1 ; Bremsen
      Object\Speed - Object\BrakeForce * TimeLoop
      If Object\Speed <= 0
        If Not NextElement(WayPoint())
          FirstElement(WayPoint())
        EndIf
        Object\Speed = 0
        Object\Angle = Angle(WayPoint()\X - Object\X, WayPoint()\Y - Object\Y)
        Object\State = 0
      EndIf
  EndSelect
  
  ;berechnet die Bewegung anhand der aktuellen Geschwindigkeit
  Object\X + Cos(Object\Angle * #PI / 180) * Object\Speed * TimeLoop
  Object\Y + Sin(Object\Angle * #PI / 180) * Object\Speed * TimeLoop
  
  ;Zeichnen der Wegpunkte und des sich bewegenden Objektes
  ClearScreen(0)
  StartDrawing(ScreenOutput())
    *Current = @WayPoint()
    ForEach WayPoint()
      Circle(WayPoint()\X, WayPoint()\Y, 1, $ffff00)
    Next
    ChangeCurrentElement(WayPoint(), *Current)
    Box(Object\X - 2, Object\Y - 2, 5, 5, $0000ff)
  StopDrawing()
  FlipBuffers()
  
  ;Berechnung der benötigten Zeit für den letzten Schleifendurchlauf
  TimeLoop = (ElapsedMilliseconds() - LoopTimer) / 1000
  LoopTimer = ElapsedMilliseconds()
Until WaitWindowEvent(10) = #PB_Event_CloseWindow
MFG PMV

Verfasst: 12.10.2006 01:18
von Agent
Okay. vielen Dank erstmal, schaue da morgen (nachher) mal rein.

Verfasst: 12.10.2006 07:14
von #NULL
Neee #null, das ist nicht das was ich suche, da könnt ich ja gleich x=x-2 schreiben oder wie in meinem beispiel move_x / 2 (wäre ähnlich) Wink

das Fenster in dem Beispiel soll sich erst schnell und gegen Ende langsamer bewegen.
:? genau macht doch mein code. falls dein rechner zu schnell ist erhöhe halt das delay, oder mach aus der 98 eine 99. ich kann mir ehrlich nicht vorstellen wie es besser aussehen könnte. jedenfalls hat mein code nichts mit x=x-2 oder derlgeichen zu tun.
..aber is ja dein ding.

Verfasst: 12.10.2006 09:04
von Froggerprogger
Ein Delay sollte niemals verwendet werden, um Geschwindigkeiten/Positionierungen usw. von Objekten zu steuern. Das habe ich in meinem Code zwar auch gemacht, aber nur der Bequemlichkeit halber.
Eigentlich sollte man das genauso machen, wie PMV: Positionierungen nur abhängig von der verstrichenen Zeit. Denn dann ist egal, ob das Delay auch mal länger dauert oder unregelmäßig ist, trotzdem bleibt die Bewegung flüssig, bzw. auch wenn ruckelig wird stets die korrekte Position angezeigt. Die Anzeige ist dann zwar vielleicht mal ruckelig bei zu wenig Frames, aber die Objektbewegung bleibt richtig.

Wenn das Objekt wirklich einem physikalischem Objekt entspricht, z.B: ein Raumschiff, etc. dann ist der Ansatz von PMV auch sicherlich der bessere. Es ist dann die bessere Modellierung der Realität und daher lässt sich da nachher intuitiver dran rumbasteln, z.B. die Bremskraft verändern etc. Auch kann man dort leicht eine Schwerkraft einbauen, usw.
Mein Ansatz durch direkte Berechnung der Position ist eher für sowas wie z.B. Fliegende Icons, die in einen Papierkorb hüpfen, o.ä.

Verfasst: 12.10.2006 09:05
von Kaeru Gaman
....scheint ja so, als wären schon alle benötigten lösungen vorhanden...

ich bin sowieso besser auf screens...
wenn ich das mit nem fenster machen soll, brech ich mir einen ab...

Verfasst: 12.10.2006 10:31
von #NULL
@Froggerprogger
ich stimme dir in allem zu. timer sind delays vorzuziehen, und ich wollte auch nicht unbedingt physik simulieren. ich hab einfach die frage mit am wenigsten aufwand beantwortet: wie bewege ich ein fenster nach links, wobei es auf dem weg langsamer werden soll:

Code: Alles auswählen

schleife
  x.f = x*99/100.0
schleifenende
Agent's antwort hat auf mich nur so gewirkt, als hätte er es gar nicht richtig ausprobiert, bzw verstanden.

Verfasst: 12.10.2006 13:43
von #NULL
weder offtopic noch nützlich 8) :

Code: Alles auswählen

w=180
h=80
hWin=OpenWindow(0, 500,300,w,h, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(hWin)
  TextGadget(0, 5,10, w-10,50, "Völlegefühl !"+#LF$+" Kennen Sie auch das auch?", #PB_Text_Center)
  ButtonGadget(1, 10,50, 70,20,"nu")
  ButtonGadget(2, 100,50, 70,20,"nö")

SystemParametersInfo_(#SPI_GETWORKAREA,0,@DesktopWorkArea.RECT,0)
dh=DesktopWorkArea\Bottom-h-30

posY.f
direction.l=0
count.f
a.f=#PI/2
Repeat
  event=WindowEvent()
  eg=EventGadget()
  Select event
    Case #PB_Event_CloseWindow
      quit=1
    Case #PB_Event_Gadget
      Select eg
        Case 1
          direction=-1
        Case 2
          direction=1
      EndSelect
  EndSelect

  If direction
    posX=WindowX(0)
    posY=WindowY(0)
    count=posY
    If direction=-1 : count=-count+dh : EndIf
    While count>2
      posY=Abs(count*Sin(a))
      If direction=-1 : posY=dh-posY : EndIf
      
      ResizeWindow(0,posX,posY,w,h)
      While WindowEvent() Or timer>ElapsedMilliseconds(): Wend
      timer=ElapsedMilliseconds()+13

      count*90/100
      a+0.3*direction
    Wend
    a.f=#PI/2
    timer=0
    direction=0
  EndIf
Until quit

Verfasst: 12.10.2006 15:52
von Macros
Noch ein Beitrag von mir, ganz nach dem Topic:

Code: Alles auswählen

OpenWindow(1,200,200,400,400,"test")
CreateGadgetList(WindowID(1))
TextGadget(10,0,0,100,30,"New Window X")
TextGadget(11,0,30,100,30,"New Window Y")
StringGadget(1,100,00,100,30,"")
StringGadget(2,100,30,100,30,"")
ButtonGadget(0,200,00,100,60,"Submit")
Targetx=WindowX(1)
targety=WindowY(1)


Repeat
  event=WindowEvent()
  
  If event=#PB_Event_Gadget
    If EventGadget()=0
      Targetx=Val(GetGadgetText(1))
      targety=Val(GetGadgetText(2))
    EndIf
  EndIf
  
  
  If WindowX(1)<>target Or WindowY(1)<>target
    If move=0
      xmove=(Targetx-WindowX(1))/2
      ymove=(targety-WindowY(1))/2
      move=1
      xl=WindowX(1)
      yl=WindowY(1)
    EndIf
  EndIf
  
  
  If move=1
    winkel+1
    Delay(20)
    bogenmass.f=winkel*3.1415926/180
    x.l=xl-Cos(bogenmass)*xmove+xmove
    y.l=yl-Cos(bogenmass)*ymove+ymove
    SetWindowPos_(WindowID(1),HWND_NOTOPMOST,x,y,400,400,#SWP_NOZORDER)
    If winkel=180
      move=0
      winkel=0
    EndIf
  EndIf
  
  
Until event=#PB_Event_CloseWindow

Verfasst: 12.10.2006 18:07
von PMV
Froggerprogger hat geschrieben:Eigentlich sollte man das genauso machen, wie PMV: Positionierungen nur abhängig von der verstrichenen Zeit. Denn dann ist egal, ob das Delay auch mal länger dauert oder unregelmäßig ist, trotzdem bleibt die Bewegung flüssig, bzw. auch wenn ruckelig wird stets die korrekte Position angezeigt. Die Anzeige ist dann zwar vielleicht mal ruckelig bei zu wenig Frames, aber die Objektbewegung bleibt richtig.
Bei starkem ruckeln kann das Objekt den Zielpunkt um einiges verfehlen
... diese sicherheitsabfrage am ende hab ich mir aber gesparrt, ist aber
normal unumgänglich, da es sonnst zu starken Bugs kommen kann ... ich
weis wovon ich rede :mrgreen: :lol:

Die "Angle"-Prozedur könnte man auch besser in Bogenmaß machen,
dann würde das Umrechnen in Cos() und Sin() wegfallen. Aber ich war zu
faul die vorhandene Prozedur zu ändern :mrgreen:
Kaeru Gaman hat geschrieben:ich bin sowieso besser auf screens...
Der Fragende hat das ganze nicht auf Fenster begrenzt :wink:

MFG PMV