Page 1 of 1

Programming principles

Posted: Sat Feb 22, 2025 12:22 pm
by jak64
Hello everyone,
My question may seem "basic" to you but I ask it anyway.

I read, somewhere on the forum, that in a program, we should only have one loop, like while...Wend and manage all the events in this loop.
I would like to point out that my question concerns a game.

In a game, I can have a presentation screen, then a start of game screen, then a game screen, then a game, then the end of the game...

On each of these screens, I will have different events, with buttons or other...

How do I handle this in a single loop, for example I can have a Next button in multiple screens, how do I know, if I only have one loop, which screen the next button refers to?

My idea, for me, is to make a screen, with a loop, for each case, a screen for presentation, with a while...Wend loop and a Flipbuffer(), a screen for the start of the game, with a While...Wend loop and a Flipbbuffer(), etc., each screen management being in a specific procedure.

Please give me your opinions.

A+

// Moved from "Coding Questions" to "General Discussion" (Kiffi)

Re: Programming principles

Posted: Sat Feb 22, 2025 2:08 pm
by moulder61
@jak64,

As no kind of PB expert, or even any kind of programmer if I'm honest, I've had the same question.
Sometimes the logic of how it all fits together doesn't quite work in my head :?:
Anyway, having followed the snake game tutorial by PureProgramming on YouTube, I started to understand the concept of state machines and key handling/mouse handling routines working together.
Basically, in the main loop, check for the state i.e. TitleScreen, GameScreen, GameOver whatever, then in the key/mouse handling routines do the same and therefore you isolate it somewhat.
For example, there's no point checking for sprite collisions on the TitleScreen(probably). Etc.
There are way more proficient programmers on here, but as a beginner myself, that's how I've fumbled along in trying to get a game or two working.
I hope that makes some sense?

Moulder.

Re: Programming principles

Posted: Sat Feb 22, 2025 2:28 pm
by miso
we should only have one loop
I'm not sure about this. You can have as many main loops, as many you want, but use only one at a time. I usually create a module that initializes screen and mouse etc, and separate modules for splash screen/intro, mainmenu loop, gameloop, shopping screen. I then switch back and forth of them. I usually create a 0 layer module that holds only the common global data, that can be accessed by the other modules.

Modules has not been invented for this, but works like a charm, if you want to put different game behaviors to different loops.

Re: Programming principles

Posted: Sat Feb 22, 2025 4:18 pm
by jak64
Thank you molder61 and miso for your answers.

It's true that there are certainly several ways to proceed but, for my part, I prefer to write procedures independent of the main loop, with a logical test in the main loop which allows me to know where I am, for example, a boolean "Make_presentation" which will execute the procedure Make Presentation() if it is #true. Another boolean "Start_Game" which will execute the procedure "StartGame() when it is #true, etc...

This allows me to develop each procedure independently of the others and not regress on my main code or other procedures that already work.

But I am interested in any other good practice...

See you soon on this Forum which has taught me so many things... And I love PureBasic :D :D :D :D :D

Re: Programming principles

Posted: Sat Feb 22, 2025 4:26 pm
by miso
We talk about the same thing. I just hid my procedure inside separate modules (in separate files.) :)

Re: Programming principles

Posted: Sat Feb 22, 2025 4:30 pm
by jak64
Hello miso,
Do you mean that every procedure is in a .pbi?

If so, what is the point?

Re: Programming principles

Posted: Sat Feb 22, 2025 4:54 pm
by moulder61
@jak64

What you describe is basically what I said about state machines. Select the state in the main loop then jump to the relevant procedure.
Obviously you would initialise the state to #TitleScreen to start with, then when in the TitleScreen() procedure you would check for a button click or keypress then change the state accordingly, maybe to #Quit or #Playing or #Credits etc?

Code: Select all

Select state
  Case #TitleScreen
    MouseHandler()  ; Check mouse position, button clicks etc.
    TitleScreen()        ; Do TitleScreen related stuff.
  Case GameOver
    MouseHandler()
    GameOver()
EndSelect
Something like that maybe?
I don't pretend to understand it fully, but it works. :P

Moulder.

Re: Programming principles

Posted: Sat Feb 22, 2025 8:29 pm
by AZJIO

Re: Programming principles

Posted: Mon Feb 24, 2025 12:03 pm
by benubi
There are a few "game loop" theories that are not specific to PureBasic.

Inside your loop you will have to branch/call a few subroutines in any state (like check Windows Events).

One classic way is to use a "game state"

Code: Select all

; incomplete 
Enumeration
  #Game_Init
  #Game_Loading
  #Game_MainMenu
  #Game_Config
  #Game_Running
  #Game_Finished
  #Game_Hiscores
  #Game_ServerLoading ; or client or connecting to server etc
  ; ...
  EndEnumeration
  
  Global GameState
  Global EXIT
  
  Procedure RenderGameState()
    Select GameState
      ; ...
      Case #Game_Loading
         DisplayLoadScreen()
      Case #Game_Config
         EditConfiguration()
      Case #Game_Running
      	 DrawManyThings()
      	 ; ...
    EndSelect
    FlipBuffers()
  EndProcedure
  
  Procedure MainLoop()
    Repeat
  	DoWindowEvents()
  	DoUserInputs()
  	RenderGameState()  
    Until EXIT
  EndProcedure
  
  

Re: Programming principles

Posted: Mon Feb 24, 2025 12:05 pm
by benubi
Additionally your DoUserInputs() will have to check in what game state it is, probably. This way you can also separate the event handling and presentation.

Re: Programming principles

Posted: Mon Feb 24, 2025 8:18 pm
by miso
Do you mean that every procedure is in a .pbi?
If so, what is the point?
I put parts of the game into separate modules, and those modules goes into different files. I can use the same procedure names to create a similar layout
but can get different behavior. For a small game like snake for example, its not necessary, for complex things I found this to be the most convenient.

I often use a short named module like GM (as game manager) at layer 0, that holds only public global variables that can be accessed by the other modules.

Heres a pseudo code, because I'm not sure I can explain it better than this:

Code: Select all

;PSEUDO CODE

DeclareModule mainmenu
  Declare run()
EndDeclareModule

Module mainmenu
  Declare init()
  Declare Destroy()
  Declare update()
  Declare draw()
  
  Procedure init()
    ;get resources, load some graphics, allocates some memory for the main menu
  EndProcedure
  
  Procedure destroy()
    ;releases the resources not needed when switching to another screen/game part
  EndProcedure
  
  Procedure update()
    ;checks for window events in windowed mode, check user input, mouse, etc
    ;if gm::state.s = "button_new_game" : destroy() : newgame::run() : endif
  EndProcedure
  
  Procedure draw()
    ;draw the main menu elements
  EndProcedure

 
  Procedure run()
    init()
    Repeat
      update()
      draw()
    ForEver
  EndProcedure
  
  
EndModule

;--------------

DeclareModule wilderness
  Declare run()

EndDeclareModule

Module wilderness
  Declare init()
  Declare Destroy()
  Declare update()
  Declare draw()
  
  Procedure init()
    ;get resources, load some graphics, allocates some memory etc
  EndProcedure
  
  Procedure destroy()
    ;releases the resources not needed when switching to another screen/game part
  EndProcedure
  
  Procedure update()
    ;if gm::state.s = "button_enter_city" : destroy() : city::run() : endif
  EndProcedure
  
  Procedure draw()
  EndProcedure
  
  Procedure run()
    init()
    Repeat
      update()
      draw()
    ForEver
  EndProcedure
  
EndModule 

;--------------

DeclareModule city
  Declare run()
EndDeclareModule

Module city
  Declare init()
  Declare Destroy()
  Declare update()
  Declare draw()
  
  
  Procedure init()
    ;get resources, load some graphics, allocates some memory etc
  EndProcedure
  
  Procedure destroy()
    ;releases the resources not needed when switching to another screen/game part
  EndProcedure
  
  
  
  Procedure update()
    ;if gm::state.s = "button_enter_shop" : destroy() : shop::run() : endif
  EndProcedure
  
  Procedure draw()
    ;code to draw the city screen
  EndProcedure

  Procedure run()
    init()
    Repeat
      update()
      draw()
    ForEver
  EndProcedure
  
EndModule 

Re: Programming principles

Posted: Tue Feb 25, 2025 10:49 am
by jak64
Hello and thank you all for your responses.