[game] follow path

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

[game] follow path

Message 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 ;).
Avatar de l’utilisateur
majikeyric
Messages : 602
Inscription : dim. 08/déc./2013 23:19
Contact :

Re: [game] follow path

Message 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é...
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: [game] follow path

Message 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 ^^
Avatar de l’utilisateur
majikeyric
Messages : 602
Inscription : dim. 08/déc./2013 23:19
Contact :

Re: [game] follow path

Message 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
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

Re: [game] follow path

Message 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 !
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: [game] follow path

Message 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.
Avatar de l’utilisateur
Ar-S
Messages : 9540
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: [game] follow path

Message par Ar-S »

Tu nous fait un petit beware planet earth ?
Finis nous Arkeos Chronicle ! Moi je veux le tester ! :mrgreen:
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
G-Rom
Messages : 3641
Inscription : dim. 10/janv./2010 5:29

Re: [game] follow path

Message 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 

Avatar de l’utilisateur
blendman
Messages : 2017
Inscription : sam. 19/févr./2011 12:46

Re: [game] follow path

Message 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 ^^).
Répondre