Page 1 sur 1

[game] follow path

Publié : mer. 12/mars/2014 19:09
par blendman
salut

En ce moment, je réalise un petit tower defense (pour android).
En gros, j'ai des mobs qui suivent un chemin.

J'utilise ça comme technique, je me demandais s'il y avait une technique mieux ou plus optimisée ?

Code : Tout sélectionner


If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0
  MessageRequester("Error", "Sprite system can't be initialized", 0)
  End
EndIf


Structure Smob
  
  x.d
  y.d
  node.a
  speed.d
  time.i
  
EndStructure
Global Dim Mob.sMob(15)

For i = 0 To 15
  
  mob(i)\x = -100
  mob(i)\y = 256
  mob(i)\time = 80 * i  
  mob(i)\speed = 0.5 +Random(10)/10
  
Next i



Structure sNode
  
  x.w
  y.w
  
EndStructure
nbnode = 5
Global Dim Path.sNode(nbnode)

; j'ajoute les points du chemin pour tester
Path(0)\x = 0
path(0)\y = 256

Path(1)\x = 256
path(1)\y = 256

Path(2)\x = 256
path(2)\y = 384

Path(3)\x = 512
path(3)\y = 384

Path(4)\x = 512
path(4)\y = 256

Path(5)\x = 800
path(5)\y = 256



OpenWindow(0, 0, 0, 800, 600, "Path test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

If OpenWindowedScreen(WindowID(0), 0, 0, 800, 600)
  
EndIf

; le mob
w = 32
CreateSprite(0, w,w)
If StartDrawing(SpriteOutput(0))
  Box(0,0,w,w,RGB(125,125,125))
  StopDrawing()
EndIf

; le point du path
CreateSprite(1, 16,16)
If StartDrawing(SpriteOutput(1))
  Box(0,0,16,16,RGB(255,255,125))
  StopDrawing()
EndIf

Repeat
  
  event = WindowEvent()
  
  ClearScreen(RGB(60,60,60))
  
  For i = 0 To nbnode
    DisplaySprite(1, path(i)\x-8, path(i)\y-8)
  Next i
  
  For i = 0 To 15
    
    If mob(i)\time <= 0
      
      If mob(i)\x > path(mob(i)\node)\x
        mob(i)\x - mob(i)\speed
        If mob(i)\x < path(mob(i)\node)\x
          mob(i)\x = path(mob(i)\node)\x
        EndIf  
      ElseIf mob(i)\x < path(mob(i)\node)\x
        mob(i)\x + mob(i)\speed
        If mob(i)\x > path(mob(i)\node)\x         
          mob(i)\x = path(mob(i)\node)\x
        EndIf          
      EndIf 
      
      If mob(i)\y > path(mob(i)\node)\y
        mob(i)\y - mob(i)\speed
        If mob(i)\y < path(mob(i)\node)\y
          mob(i)\y = path(mob(i)\node)\y
        EndIf 
      ElseIf mob(i)\y < path(mob(i)\node)\y
        mob(i)\y + mob(i)\speed
        If mob(i)\y > path(mob(i)\node)\y
          mob(i)\y = path(mob(i)\node)\y
        EndIf  
      EndIf 
      
      If mob(i)\y = path(mob(i)\node)\y And mob(i)\x = path(mob(i)\node)\x
        If mob(i)\node < nbnode
          mob(i)\node = mob(i)\node + 1
        EndIf
      EndIf
      
      DisplaySprite(0, mob(i)\x-w/2, mob(i)\y-w/2)
    Else
      mob(i)\time = mob(i)\time - 1
    EndIf
    
    
    
  Next i
  
  If StartDrawing(ScreenOutput())
    
    For i = 0 To 15
      ;t$ = Str(Round(mob(i)\y,2) ) + "/" +Str( path(mob(i)\node)\y)
      t$ = StrD(mob(i)\speed)
      DrawText(mob(i)\x-w/2, mob(i)\y-w, t$)
      
    Next i
    
    StopDrawing() 
  EndIf
  
  
  Delay(1)
  FlipBuffers()
  
  
Until event = #PB_Event_CloseWindow

Si vous avez une autre idée, un truc mieux ou plus optimisé, n'hésitez pas à poster ;).

Re: [game] follow path

Publié : mer. 12/mars/2014 20:42
par majikeyric
Je ne comprends pas ce qui ne fonctionne pas ?
en forçant la vitesse à 0.3 par exemple, ça marche aussi...

*EDIT*: ah bah l'énoncé du premier post a changé...

Re: [game] follow path

Publié : mer. 12/mars/2014 20:54
par blendman
majikeyric a écrit :Je ne comprends pas ce qui ne fonctionne pas ?
en forçant la vitesse à 0.3 par exemple, ça marche aussi...

*EDIT*: ah bah l'énoncé du premier post a changé...
oui, j'ai trouvé entre temps, mais je me demande si on ne peut pas optimiser le code ^^

Re: [game] follow path

Publié : mer. 12/mars/2014 21:17
par majikeyric

Code : Tout sélectionner

For i = 0 To 15
	
	If mob(i)\time <= 0
		
		If Abs(mob(i)\x-path(mob(i)\node)\x)<=mob(i)\speed
			mob(i)\x = path(mob(i)\node)\x
		EndIf
		
		If mob(i)\x > path(mob(i)\node)\x
			mob(i)\x - mob(i)\speed
		ElseIf mob(i)\x < path(mob(i)\node)\x
			mob(i)\x + mob(i)\speed
		EndIf
		
		If Abs(mob(i)\y-path(mob(i)\node)\y)<=mob(i)\speed
			mob(i)\y = path(mob(i)\node)\y
		EndIf
		
		If mob(i)\y > path(mob(i)\node)\y
			mob(i)\y - mob(i)\speed
		ElseIf mob(i)\y < path(mob(i)\node)\y
			mob(i)\y + mob(i)\speed
		EndIf
		
		If mob(i)\y = path(mob(i)\node)\y And mob(i)\x = path(mob(i)\node)\x
			If mob(i)\node < nbnode
				mob(i)\node = mob(i)\node + 1
			EndIf
		EndIf
		
		DisplaySprite(0, mob(i)\x-w/2, mob(i)\y-w/2)
	Else
		mob(i)\time = mob(i)\time - 1
	EndIf
	
Next i

Re: [game] follow path

Publié : jeu. 13/mars/2014 3:49
par graph100
tout dépend de si tu as prévu qu'il puisse y avoir des obstacles sur le chemin des ennemis ! Ca change tout !

Re: [game] follow path

Publié : jeu. 13/mars/2014 8:38
par blendman
majikeyric : merci pour pour ton code, ça pourrait être une solution. Faudra que je teste pour voir si c'est plus rapide ;).
tout dépend de si tu as prévu qu'il puisse y avoir des obstacles sur le chemin des ennemis ! Ca change tout !
pour ce jeu-ci, il n'y aura pas d'obstacle, le chemin est prédéfini et ne change pas ensuite. On peut placer des tours sur des cases autorisées mais pas sur le chemin sur lequel les mobs avancent.

Re: [game] follow path

Publié : jeu. 13/mars/2014 17:55
par Ar-S
Tu nous fait un petit beware planet earth ?
Finis nous Arkeos Chronicle ! Moi je veux le tester ! :mrgreen:

Re: [game] follow path

Publié : jeu. 13/mars/2014 22:18
par G-Rom
Le problème c'est que ton "path" est trop lié à ton mobs , c'est pas souple , et pas facile d'emploi.
Pour ce genre de "problème" , j'utilise l'interpolation linéaire entre 2 points, dans l'exemple que je vais te donner, c'est basé sur des clé de temps ( comme un logiciel d'animation avec des keyFrame ) , tu rajoutes des positions à atteindre à un temps T , et tu laisse faire les fonctions , il y a 2 modes de déplacement , linéaire & cosine , Dans un logiciel d'animation cela correspondrais à l'icone "ligne droite" pour les déplacement linéaire, et ligne courbe pour les déplacement cosine, pour ce dernier , les virage sont donc moins brute , le point ralentit à l'arrivé , puis accélère ensuite en fonction du temps qui lui reste à atteindre le prochain point.
Tu peu utilisé aussi cette base de code pour animé n'importe quoi avec du temps & des clé ( des rotations , la mise a l'echelle , le pitch d'un son , etc..... )

Amuse toi bien ;)

Code : Tout sélectionner

;
; HELPER FUNCTION
;
Procedure.f Min(a.f,b.f)
   If a<b
      ProcedureReturn a
   EndIf
   ProcedureReturn b
EndProcedure

;
; HELPER FUNCTION
;
Procedure.f Max(a.f,b.f)
   If a>b
      ProcedureReturn a
   EndIf
   ProcedureReturn b
EndProcedure


; Structure représentant un point dans l'espace
; 
;
Structure vector2d
  x.d
  y.d
EndStructure

Procedure.b vector2dAreClose(*A.vector2d , *B.vector2d)
  tolerance.f = 0.01
  If (Abs(*A\x - *B\x) < tolerance) And (Abs(*A\y - *B\y) < tolerance)
    ProcedureReturn #True 
  Else 
    ProcedureReturn #False 
  EndIf 
EndProcedure

;
; HELPER MACRO
;
Macro linearInterpolation(a,b,t)
  (a*(1-t)+b*t)
EndMacro

;
; HELPER MACRO
;
Macro t2(t)
  ((1-Cos(t*#PI))/2)
EndMacro

;
; HELPER MACRO
;
Macro CosineInterpolation(a,b,t)
  a * (1-t2(t))+b*t2(t)
EndMacro

Enumeration 
  #LINEAR_INTERPOLATION
  #COSINE_INTERPOLATION
EndEnumeration

Procedure Interpolation1D(*value, min.d, max.d ,t.d, mode.l = #LINEAR_INTERPOLATION)
  Select mode
    Case #LINEAR_INTERPOLATION
      PokeD(*value, linearInterpolation(min,max,t))
    Case #COSINE_INTERPOLATION
      PokeD(*value, CosineInterpolation(min,max,t))
  EndSelect
EndProcedure

Procedure Interpolation2D(*vector.vector2d, *min.vector2d, *max.vector2d ,t.d, mode.l = #LINEAR_INTERPOLATION)
  Select mode
    Case #LINEAR_INTERPOLATION
      *vector\x = linearInterpolation(*min\x,*max\x,t)
      *vector\y = linearInterpolation(*min\y,*max\y,t)
    Case #COSINE_INTERPOLATION
      *vector\x = CosineInterpolation(*min\x,*max\x,t)
      *vector\y = CosineInterpolation(*min\y,*max\y,t)
  EndSelect
EndProcedure

; 
;
;
Structure waypoint
  position.vector2d
  time.d
EndStructure

Structure path
  List point.waypoint()
EndStructure

;
;
;
Procedure.i CreateWayPath()
  *p.path = AllocateMemory(SizeOf(path))
  If *p
    InitializeStructure(*p,path)
    ProcedureReturn *p
  EndIf
  ProcedureReturn #Null 
EndProcedure

;
;
;
Procedure AddWayPoint(*p.path, x.d, y.d, time.d)
  If *p
    AddElement(*p\Point())
      *p\Point()\position\x = x
      *p\Point()\position\y = y
      *p\Point()\time       = time
  EndIf 
EndProcedure

;
;
;
Procedure.d GetWayPointX(*p.path,index.l)
  If *p
    PushListPosition(*p\Point())
    SelectElement(*p\Point(),index)
    result.f = *p\Point()\position\x
    PopListPosition(*p\Point())
    ProcedureReturn result 
  EndIf 
EndProcedure

;
;
;
Procedure.d GetWayPointY(*p.path,index.l)
  If *p
    PushListPosition(*p\Point())
    SelectElement(*p\Point(),index)
    result.f = *p\Point()\position\y
    PopListPosition(*p\Point())
    ProcedureReturn result 
  EndIf 
EndProcedure



; Structure representant un "suiveur"
; de path
;
Structure pathFollower
  *follow.path
  position.vector2d
  moveTo.vector2d
  index.l
  timer.l
EndStructure

;
;
;
Procedure.i createFollower(*follow.path)
  *f.pathFollower = AllocateMemory(SizeOf(*f))
  If *f
    FirstElement(*follow\Point())
      *f\position\x = *follow\Point()\position\x
      *f\position\y = *follow\Point()\position\y
    NextElement(*follow\Point())
      *f\moveTo\x = *follow\Point()\position\x
      *f\moveTo\y = *follow\Point()\position\y
      *f\timer      = 0
      *f\follow     = *follow
      *f\index      = 0
      FirstElement(*f\follow\Point())
      ProcedureReturn *f
  EndIf 
    
  ProcedureReturn #Null 
EndProcedure

;
;
;
Procedure moveFollower(*follower.pathFollower, time.i)
  If *follower
    With *follower
      
      ; update time
      ;
      \timer      = time
      nextTime.i  = 0
                  
  
      While \timer >= nextTime
        PushListPosition(*follower\follow\Point())
          If NextElement(*follower\follow\Point()) = 0 
            ProcedureReturn 0
          EndIf 
          nextTime = *follower\follow\Point()\time 
          
          *Follower\moveTo\x = *Follower\follow\Point()\position\x
          *Follower\moveTo\y = *Follower\follow\Point()\position\y
          
        PopListPosition(*follower\follow\Point())
        
        If \timer >= nextTime
          \index + 1
        EndIf 
                
        SelectElement(*follower\follow\Point(),\index)
      Wend 
 
       A.i = *follower\follow\Point()\time 
       B.i = nextTime
       t.f = (\timer - Min(A,B)) / (Max(A,B)-Min(A,B))
       
       Interpolation2D(@\position,@*follower\follow\Point()\position,@\moveTo,t, #LINEAR_INTERPOLATION) ; Change avec #COSINE_INTERPOLATION

    EndWith
  EndIf 
EndProcedure

Procedure resetFollower(*f.pathFollower)
  If *f
     FirstElement(*f\follow\Point())
      *f\position\x = *f\follow\Point()\position\x
      *f\position\y = *f\follow\Point()\position\y
    NextElement(*f\follow\Point())
      *f\moveTo\x = *f\follow\Point()\position\x
      *f\moveTo\y = *f\follow\Point()\position\y
      *f\timer      = 0
      *f\index      = 0
      FirstElement(*f\follow\Point())
  EndIf 
EndProcedure


Procedure.f GetFollowerX(*f.pathFollower)
  If *f
    ProcedureReturn *f\position\x
  EndIf 
EndProcedure

Procedure.f GetFollowerY(*f.pathFollower)
  If *f
    ProcedureReturn *f\position\y
  EndIf 
EndProcedure



;-test
OpenWindow(0,0,0,1024,768,"")
CanvasGadget(0,0,0,1024,768)



*MyPath = CreateWayPath()
  AddWayPoint(*MyPath,100,500,0)    ; 100x100 au temp 0ms
  AddWayPoint(*MyPath,200,200,500) ; 200x200 au temp 2000ms
  AddWayPoint(*MyPath,400,400,1000) ; 200x200 au temp 2000ms
  AddWayPoint(*MyPath,800,300,1500) ; 200x200 au temp 2000ms
  AddWayPoint(*MyPath,900,600,2000) ; 200x200 au temp 2000ms
  
*Follower = createFollower(*MyPath)
simulatedTime.f = 0
While #True 
  
  If WindowEvent() = #PB_Event_CloseWindow
    Break
  EndIf 
  
  StartDrawing(CanvasOutput(0))
  Box(0,0,OutputWidth(), OutputHeight(), $FFFFFF)
  
  For p = 0 To 4
    x.f = GetWayPointX(*MyPath,p)
    y.f = GetWayPointY(*MyPath,p)
    Circle(x,y,4,$999999)
  Next 
  
  moveFollower(*Follower,simulatedTime)
  Circle( GetFollowerX(*Follower),GetFollowerY(*Follower),4,$FF)
  
  simulatedTime + 10
  If simulatedTime => 2000
    simulatedTime=0
    resetFollower(*Follower)
  EndIf 

  StopDrawing()

Wend 


Re: [game] follow path

Publié : ven. 14/mars/2014 18:08
par blendman
Merci g-rom pour ton code, c'est intéressant.
Par contre, je ne sais pas si je pouvoir m'en servir, car comme j'avais préciser que c'était pour un jeu sous android, je dois ensuite adapter le code (pour AGK), et je ne pense pas que j'ai accès aux pointeurs ni aux listes chainées, ni même aux maps (hélas ^^).