Page 1 sur 1

[games] Events et actions

Publié : jeu. 19/sept./2013 19:28
par blendman
salut

J'essaie de tester un système d'event et d'action façon Game maker (pour ceux qui connaissent).

Voici le code que j'ai commencé pour étudier ça (je veux un code très simple) :

Code : Tout sélectionner

;{ enumeration

#window_main  = 0

; Fixé par le créateur du jeu, dans PGC
#canvas       = 0
#WindowWidth  = 640
#WindowHeight = 480
#FPSMax         = 40 ; le FPS souhaité (approximation à environ -7)

Enumeration EventObject
  
  ; les events de chaque objets
  
  #EventCreate
  #EventStep
  #EventDraw
  #EventDestroy
  
EndEnumeration

Enumeration EventGame
  
  ; les events généraux du jeu
  
  #EvtGame_GameStart
  #EvtGame_GameEnd
  #EvtGame_RoomStart
  #EvtGame_RoomEnd
  #EvtGame_Step
  
EndEnumeration

;}

;{ structure

Structure ST_object
  
  Name$
  
  ; event
  Event.i
  EventCreate.a
  EventDestroy.a
  
  ; pour certains event, pour vérifier si on les a ajouté
  EventDraw.a
  
  ; autre paramètres
  Life.w  
  SpriteId.i
  X.w
  Y.w
  
EndStructure
Global NewList Object.ST_object()

;}

;{ variable

Global ObjectId.i, Exit.a, EventGame.a, Room.a, Color.i

;}

;{ Declare
Declare RoomGotoNext()
Declare GameEnd()
;}

;{ procedures

;{ Event Actions pour objets

Procedure EO_Create(List Obj.ST_object())
  
  If Obj()\EventCreate = 0
    Debug  "Create " + Obj()\Name$
    Obj()\EventCreate = 1
    Obj()\Event = #EventStep
  EndIf 
  
EndProcedure

Procedure Instance_Create(List Obj.ST_object())
  
  AddElement(Obj())
  
  With Obj()
    
    \Name$  = "Obj_" + ObjectId
    ObjectId + 1
    \Life   = 20 + Random(20)    
    \X      = Random(#WindowWidth)
    \Y      = Random(#WindowHeight)
    \Event  = #EventCreate
    
    \EventDraw = 1 ; on a ajouté un code dans l'event draw
    
    ; le sprite
    \SpriteId = CreateSprite(#PB_Any, 64, 64)
    
    If \SpriteId
      
      If StartDrawing(SpriteOutput(\SpriteId))
        Box(0, 0, 64, 64, RGB(Random(255), Random(255), Random(255)))
        StopDrawing()
      EndIf
      
    EndIf
    
  EndWith
  
EndProcedure

Procedure EO_Step(List Obj.ST_object())
  
  ;{ begin step
  
  ;}
  
  ;{ step
  
  ; un code par exemple
  If Obj()\X > - 64
    Obj()\X - 1
  Else
    Obj()\x = #WindowWidth
  EndIf
  
  Obj()\Life - 1
  
  If Obj()\life <= 0
    Obj()\Event = #EventDestroy; <-- action destroy
  EndIf
  ;}
  
  ;{ End step

    ;}
    
EndProcedure

Procedure Instance_number(List Obj.ST_object())
  
  ProcedureReturn ListSize(obj())
 
EndProcedure

Procedure EO_Draw(List Obj.ST_object())

  DisplaySprite(Obj()\SpriteId, Obj()\x, Obj()\y)  

EndProcedure

Procedure EO_Destroy(List Obj.ST_object())
  
  If Obj()\EventDestroy = 0
    
    Obj()\EventDestroy = 1
    Debug "Destroy"
    DeleteElement(Obj(),1)    
  EndIf

  ; un code par exemple, sur l'event Destroy
  
  ; on crée une instance au hasard quand on détruit l'objet
  Hasard = Random(1)
  If hasard = 1
    Instance_Create(Obj())
  EndIf
  
  ; exemple d'ajout d'un code
  If Instance_number(Obj()) <=0 
    Debug "plus d'instance de l'objet"
    RoomGotoNext()
  EndIf
  
EndProcedure

;}

;{ Event Generaux

Procedure RoomGotoNext() 
  
  EventGame = #EvtGame_RoomEnd
  Debug "RoomGotoNext"
  
EndProcedure

Procedure GameStart()
  
  Debug "GameStart"
  
  ; On lance les actions issues du GameStart
  
  EventGame = #EvtGame_RoomStart
  
EndProcedure

Procedure GameEnd()
  
  EventGame = #EvtGame_GameEnd
  
EndProcedure

Procedure GameEvents()
  
  Select EventGame
      
     Case #EvtGame_Step
      ;{ event Step
      
      ; le step de chaque objet
          
      ForEach Object()
        
        Select Object()\event
                   
          Case #EventCreate
            EO_Create(Object())
            
          Case #EventStep
            EO_Step(Object())  
            ;EO_Draw(object())  
            
          Case #EventDestroy
            EO_Destroy(Object())
            
        EndSelect
        
      Next
        
        
      ;}     
      
    Case #EvtGame_RoomStart
      ;{ event Roomstart
      ; on fait les actions de l'event Roomstart
      Debug "Room Start: " + Room
      
      ;{ Par exemple on crée 3 objets pour commencer (avec soit un code room soit un objet ayant lui-même ce code)
      
      For i = 0 To 2
        Instance_Create(Object())
      Next i
      
      ;}
      
      ; puis on passe à l'évent Step      
      EventGame = #EvtGame_Step
      ;}
      
    Case #EvtGame_RoomEnd
      ;{ event RoomEnd
      Debug "Room End : " + Room
      If Room > 3 ; le nombre de room qu'on aura créée
        Debug "Room : " + Room
        GameEnd()
      Else
        Room + 1
        Color = RGB(50 + Random(50),50 +  Random(50),50 + Random(50))
        EventGame = #EvtGame_RoomStart
      EndIf
      ;}
      
    Case #EvtGame_GameEnd
      ;{ event GameEnd
      
      ; actions pour la fin du jeu      
      Debug "GameEnd"
      
      ; sortie de boucles
      Exit = 1
      ;}
            
  EndSelect
  
EndProcedure

Procedure GameDraw()
  
  ; event draw general
  
  ClearScreen(0)
  
  If StartDrawing(ScreenOutput())
    
    ; un fond
    Box(0, 0, #WindowWidth, #WindowHeight, Color)
    DrawText(200,0, "Room "+Str(Room) + " - Nb Objets : " + Str(ListSize(Object())))
    StopDrawing()
    
  EndIf    
  
  ForEach Object()
    If Object()\EventDraw = 0
      DisplaySprite(Object()\SpriteId, Object()\x, Object()\y)  
    Else
      EO_Draw(object())  
    EndIf    
  Next
  
  FlipBuffers()
  
  
EndProcedure


Procedure FPS()
  Shared s, fps
  
  ss  = Second(Date())
  fps + 1
  
  If s <> ss
    
    SetWindowTitle(0,"FPS: "+Str(FPS))
    s   = ss
    fps = 0
    
  EndIf
  
EndProcedure

;}

;}

;{ window & Loop

If InitSprite() : EndIf

If OpenWindow(#window_main, 0, 0, #WindowWidth, #WindowHeight, "Game Events", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
  If OpenWindowedScreen(WindowID(#window_main), 0, 0, #WindowWidth, #WindowHeight, 1, 0, 0)
        
    ; Init the game
    Room = 1
    Color = RGB(Random(50), Random(50), Random(50))
    ; on lance la première room
    
    GameStart()
    
    
    Repeat
      
      Event = WindowEvent()
      
      If ElapsedMilliseconds() > CheckTime + 1000/#FPSMax
        CheckTime = ElapsedMilliseconds()
                 
        GameEvents()
        GameDraw()
        
        FPS()
                
      Else
        
        Delay(1)
        
      EndIf
      
      ; autre : menu, par exemple ?
      
            
    Until event = #PB_Event_CloseWindow Or Exit = 1
  EndIf
  
EndIf
;}

J'aimerai avoir votre avis sur ce système.
Pensez-vous que ce soit une bonne technique (ça doit permettre d'être "générique", c'est à dire s'adapter au maximum de situation) ?

Vous pouvez aussi m'indiquer votre FPS, chez moi, ça tourne à 32/33.

Re: [games] Events et actions

Publié : jeu. 19/sept./2013 21:11
par falsam
blendman a écrit :Vous pouvez aussi m'indiquer votre FPS, chez moi, ça tourne à 32/33.
Idem 32/33 sans débug.
Intel Core2 Duo CPU 8600 2.40 Ghz, Nvidia Geforce 9600 M 512 mo. Windows 7

Re: [games] Events et actions

Publié : ven. 20/sept./2013 3:12
par flaith
Idem, 32 (avec ou sans debug) sur un LENOVO :wink:

Re: [games] Events et actions

Publié : ven. 20/sept./2013 8:57
par Ar-S
32 fps avec ou sans debug :wink:

Re: [games] Events et actions

Publié : ven. 20/sept./2013 9:50
par Good07
38 fps sur Mac.
Processeur 2,8 Ghz intel core i7
Mémoire 8GB 1067 MHz DDR3

Re: [games] Events et actions

Publié : sam. 21/sept./2013 10:18
par Mesa
32, 33 fps avec un vieux P4 monocore.

J'ai fait un petit truc à l'arrache, un patron de jeu.
Je ne sais pas s'il fonctionne dans l'état mais c'est l'idée qu'il faut prendre.

Le but de ce patron est d'avoir une boucle la plus courte possible afin d'avoir une vitesse et une réponse maximale.

Dans la boucle, on se contente de scanner une douzaine de touches du clavier et la souris. On ne peut pas faire plus rapide. On peut ajouter un IA intelligence artificielle. Et dans un second temps on execute un code en fonction de la touche.

Code : Tout sélectionner

; enumeration
; structure
; variable globale

; Declare procedure
; InitGame()
; InitLevel(n)
; InitRoom(m)
; EndRoom()
; EndLevel()
; EndGame()
; NextLife(v)
; Menu()
;   Menu_NewGame()
;  ...
; IA()
; ControleFPS()
; DisplayFPS()
; ...

;Declare procedure evenement
; OnKeyboard()
; OnMouse()
; ...
;}



; window & Loop

If InitSprite() : EndIf

If OpenWindow(#window_main, 0, 0, #WindowWidth, #WindowHeight, "Game Events", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
  If OpenWindowedScreen(WindowID(#window_main), 0, 0, #WindowWidth, #WindowHeight, 1, 0, 0)
    
    InitGame()
    
    
    While lifes < 5
      
      For Level=1 To 12
        InitLevel(level)
        
        For room =1 To 8
          InitRoom(room)
          
          Repeat
            ; Le but ici, avoir la boucle qui gère le clavier et la souris et l'ia, la plus rapide possible
            ; pour garantir la fluidié du jeu en toutes circonstances
            Event = WindowEvent()
            
            ControleFPS()
            If Event						
              OnKeyboard() ; dans onkeyboard() =>par ex.  if 'esc' then pausegame() and displaymenu() if 'space' then jump(), etc.
              OnMouse()
              IA()
              
              DisplayFPS()
              
            Else
              
              Delay(1)
              
            EndIf
            
          Until alive=#False
          nextlife()
          
        Next room
        endroom()
        
      Next level
      endlevel()
      
    Wend
    endgame()
    
  EndIf
EndIf

; Les procedures ici

; Les procédures qui impliquent un événement commence par "On" pour les repérer plus facilement.



Voilà, je ferais un truc qui ressemble à ça.

Mesa.

Re: [games] Events et actions

Publié : sam. 21/sept./2013 12:13
par Backup
Mesa a écrit :
Dans la boucle, on se contente de scanner une douzaine de touches du clavier et la souris. On ne peut pas faire plus rapide.
heu.. t'es sur ? .....


non parceque si tu met ta procedure de test claviers , dans un Thread , ben forcement tu va gagner en rapidité ... enfin j'dit ça :mrgreen:
ps: meme ton P4 est multithreadé (4 de memoire)

Re: [games] Events et actions

Publié : sam. 21/sept./2013 21:59
par stombretrooper
Je suis pas sur que blendman cherche à jouer avec les threads, sa risque de compliquer beaucoup (si tu veux un truc très rapide). Mais c'est sûr, que si le nombre d'instance à gérer dans sa boucle est très élevé, l'utilisation d'un thread accéléré beaucoup la chose

@Blendman :
Sur mon core i7, Window 8 ; 32 FPS MAIS, si tu change un peu la technique, je tourne autour des 40 FPS (comme la limite fixé par ta constante) ;

Ta boucle :

Code : Tout sélectionner

    Repeat
      
      Event = WindowEvent()
      If ElapsedMilliseconds() > CheckTime + 1000/#FPSMax
        CheckTime = ElapsedMilliseconds()
                 
        GameEvents()
        GameDraw()
        
        FPS()
                
      Else
        
        Delay(1)
        
      EndIf
    Until event = #PB_Event_CloseWindow Or Exit = 1
Déjà, 1000/#FPSMax j'en ai fait une constante : #FPSLimit
Et plutôt qu'attendre 1 millisec tant qu'on a pas atteint la limite de FPS, je regarde à la fin de la boucle combien de millisec, on doit attendre pour garder le bon FPS, et je mes directement le Delay sur cette valeur :

Mon code est le suivant (pour la même boucle) :

Code : Tout sélectionner

    Repeat
      
      Event = WindowEvent()
      CheckTime = ElapsedMilliseconds()
        
      GameEvents()
      GameDraw()
      FPS()
        
      CheckTime = ElapsedMilliseconds() - CheckTime
      If #FPSLimit - CheckTime > 0
         Delay(#FPSLimit - CheckTime)
      EndIf
    Until event = #PB_Event_CloseWindow Or Exit = 1
Je sais pas si c'est plus "propre", mais en tout cas, le FPS correspond beaucoup mieux à ce que tu fixe au départ (sur mon i7 je tiens le FPS, jusqu'à 60, où la je penses, que c'est la vitesse de refresh de l'écran qui limite le FPS :D ).

PS : je te confirme, si je désactive la synchronisation, que j'augmente la durée de vie de tes instances, et que je fixe le FPS à 250, je tourne autour d'un FPS à 180 (ton code avec ma boucle).

Re: [games] Events et actions

Publié : lun. 23/sept./2013 12:04
par blendman
Merci pour vos réponses.

En fait, l'idée c'est d'obtenir un code "générique", qui permette de réaliser n'importe quel jeu (un game maker, quoi ^^).
Je pourrais toujours ajouter une option "thread" dans les options d'optimisation du jeu, pour les gros jeux on va dire. J'ajouterai ça lorsque j'aurai codé les bases du système d'event/action.

@Strombre : merci pour ton code, je vais l'utiliser sir ça permet d'être plus proche de ce qu'on a fixé au départ ;).

Re: [games] Events et actions

Publié : mar. 24/déc./2013 10:49
par blendman
@Strombre : je viens de retester ton code, mais sur mon ordinateur, ce n'est pas fluide du tout le déplacement, ça saccade (ça accélère, ça ralentit, etc..le fps passe de 40 à 48 puis 42, etc.. ça n'arrête pas de changer ^^), alors qu'avec mon code, ça ne saccade pas c'est toujours à 32fps environ.

Tu penses qu'il y a moyen de modifier ça ?

Re: [games] Events et actions

Publié : mer. 25/déc./2013 1:13
par Jenova
Salut all et Joyeux Noël.
Le problème que tu rencontre je l'ai eu il y a bien longtemps.
Le seul remède que j'ai trouvé c'était de remplacer
CheckTime = ElapsedMilliseconds()

par
CheckTime +1000/#FPSMax

Avec ça les jeux 2D chez moi passent du stade 30 fps qui saccadent aux stade turboGTI double injection (avec attente de synchro V) et il en redemande.
Le truc que j'ai aussi remarqué (et qui est bizarre d'ailleurs) c'est que le FPSMax que l'on demande (dans le cas ou on attend la Vsync) ne doit pas dépasser les 56 voir 57 FPS et ceux, malgré que le framerate de ma carte est officiellement à 60Hz.
Par contre avec cette méthode il faut vérifier un point, le cas ou le jeu se met à ramer pour X ou Y raisons et ou le ElapsedMilliseconds() dépasse de loin le CheckTime.
Imagine que dans ton jeu un million de boulettes se baladent et font ramer le jeu. Le CheckTime avance de +1000/#FPSMax par frame mais l'heure tourne et ElapsedMilliseconds() prend un sacré écart.
Quand ton jeu fait disparaitre toutes tes boulettes et qu'il reprend une allure normale il va se mettre à booster pour rattraper l'écart.
C'est pourquoi il te faut vérifier que l'écart entre CheckTime et ElapsedMilliseconds() n'est pas plus grand que deux frames et dans le cas contraire faire un ponctuel CheckTime = ElapsedMilliseconds().
Ce CheckTime = ElapsedMilliseconds() ne surviendra que dans des cas extrêmes et passera en transparence.
@+ les zamis