Page 1 of 1

Is my approach for keypressed menu suitable.

Posted: Tue Dec 03, 2024 6:17 am
by originalbluesin
I'm new to programming and English is not my first language, please don't mind my words. The code is not exact like the code i used.

I decided is to use number (1, 2, 3 ... 9) to access the menu. For example, 1 for start game, 2 for settings, 3 for credits, 4 for quit game and escape to back up one level.
Using the modified example from love wiki.

Code: Select all

scene = {}
scene.current = "mainMenu"
function love.keypressed( key, scancode, isrepeat )
    -- pressed key on "mainMenu" scene to change scene
    if scene.current == "mainMenu" then
        if scancode == "1" then
           scene.current = "newGame"
        elseif scancode == "2" then
           scene.current = "settings"
        end
    end
end
function love.draw()
    -- draw current scene
    scene.current.draw()
end
However, the problem happen when i have sub menu like in 'settings' that use the number to access item the same way as the main menu. When i press "2" from main menu to access 'settings', and in the 'settings' also use "2" to access 'audio' menu. The scene instantly go to 'audio' menu instead of stop at the level of 'settings' sub menu.

So, i think that the keypressed is still active when the scene change from 'mainMenu' to 'settings', the scene 'settings' then change to 'audio' because the key "2" is also still pressed in 'settings' scene.

At first i use delay counter so the scene not change instantly but if the delay is too low, it's not working. If the delay is too long it made the menu lagging and not responsive. So i decide to use a flag to check if the scene is ready to change (the number key need to be released once before the scene can change again). To prevent my confusing i use global variable that can access anywhere.

Code: Select all

scene = {}
scene.current = "mainMenu"
scene.keypressed = nil
scene.readyToChange = true

function love.keypressed( key, scancode, isrepeat )
    -- pressed key on "mainMenu" scene to change scene
    if scene.current == "mainMenu" and scene.readyToChange then
        if scancode == "1" then
           scene.readyToChange = false -- flag to prevent the scene to change immediately.
           scene.keypressed = "1" 
           scene.current = "newGame"
        elseif scancode == "2" then
           scene.readyToChange = false
           scene.keypressed = "2" 
           scene.current = "settings"
        end
    elseif scene.current == "settings" and scene.readyToChange then
        if scancode == "1" then
           scene.readyToChange = false -- flag to prevent the scene to change immediately.
           scene.current = "graphicsSettings"
        elseif scancode == "2" then
           scene.readyToChange = false
           scene.keypressed = "2" 
           scene.current = "audioSettings"
        end
    end
end

function love.keyreleased( key, scancode )
    if scene.current == "newGame" and scancode == scene.keypressed then       -- released key == pressed key
    	scene.readyToChange = true
    	scene.keypressed = nil
   elseif scene.current == "settings" and scancode == scene.keypressed then
       scene.readyToChange = true
       scene.keypressed = nil
   end
end

function love.draw()
    -- draw current scene
    scene.current.draw()
end
The code is for this post and not the same as in my project (there are a lot of iteration in my project). It is working but i'm not sure if it is suitable for the game programming. Everyting is new to me. Thank you in advance from amateur developer.

Re: Is my approach for keypressed menu suitable.

Posted: Tue Dec 03, 2024 12:08 pm
by MrFariator
While something like this works for a small number potential scenes or states, it is not exactly scalable. With every added scene, submenu or thing you'll be creating an ever bigger and bigger if-else tree, which can become rather error prone and hard to maintain or read.

What you could look into is some scene management or state machines. awesome love2d repo has a few example libraries you could use.

Basically, for any given scene or state, you have only a certain amount of things a player can perform. These could be wrapped up in a function or some other data structure. A quick example below:

Code: Select all

-- table containing your scenes' button behavior
local sceneButtonBehavior = {}

-- main menu scene
sceneButtonBehavior.mainMenu = function ( key, scancode, isrepeat )
  if scancode == "1" then
    sceneButtonBehavior.current = sceneButtonBehavior.newGame
  elseif scancode == "2" then
     sceneButtonBehavior.current = sceneButtonBehavior.settings
   end
end

-- settings
sceneButtonBehavior.settings = function ( key, scancode, isrepeat )
 -- settings scene handling
end

sceneButtonBehavior.newGame = function ( key, scancode, isrepeat )
  -- newgame scene handling
end

-- set main menu as the default scene
sceneButtonBehavior.current = sceneButtonBehavior.mainMenu

function love.keypressed ( key, scancode, isrepeat )
  sceneButtonBehavior.current ( key, scancode, isrepeat )
end
This way you can make sub menus inside sub menus, because any given state the game might be in only accepts certain inputs. This is just a crude example, of course, but it should give an idea.

Re: Is my approach for keypressed menu suitable.

Posted: Tue Dec 03, 2024 12:29 pm
by originalbluesin
Thank you MrFariator for advice.

Re: Is my approach for keypressed menu suitable.

Posted: Wed Dec 04, 2024 7:11 am
by RNavega
Looks like you're missing some theory that could help you better design your programs. You don't need to strictly follow anything, but studying these things helps with giving you ideas and better habits.

From Object-Oriented Programming (OOP) comes some ideas like modularity, information hiding, interfaces, encapsulation and especially separation of concerns:
https://nalexn.github.io/separation-of-concerns/

The D.R.Y. principle also helps with forcing you to make things more modular, like building blocks:
https://blog.algomaster.io/p/design-principles

In his great example above, MrFariator used these things to define that the love.keypressed/keyreleased functions shouldn't "care" how the current scene or screen uses the keys: these functions just read the input key, maybe do some preprocessing etc, and then ask someone else (the current scene, whatever it is) to use that key for something in whatever mysterious way it wants.

PS you're using Lua which is a leaner language, but you can still do similar OOP things as in other languages. Later on you'll do a lot with "factory functions", like user airstruck talks about the end of their post in here:
viewtopic.php?p=202144#p202144

A factory function "pre-fills" a table for you, so you know that the new tables coming out of that factory function have the same set of functions and variables inside them and they can be used in the same way by other parts of your program.

Re: Is my approach for keypressed menu suitable.

Posted: Wed Dec 04, 2024 11:16 am
by originalbluesin
Thanks RNavega for the advice and learning resource. I do understand OOP but i don't like it. I prefer central management instead of OOP. Many of my friend try to convince me to use OOP but i'm not having fun programming in OOP so i stick to function style (I don't know how to call it.)

To explain, I don't like to tell a bullet x to move itself at xSpeed. I prefer to tell the bullet manager to move bullet x at xSpeed.

I do intend to making my game modular as much as possible except for something that too small to benefit from being modular.

Re: Is my approach for keypressed menu suitable.

Posted: Wed Dec 04, 2024 11:22 am
by dusoft
Not sure what central management is? Globals? Functional programming does not mean central management and global variables.

Re: Is my approach for keypressed menu suitable.

Posted: Wed Dec 04, 2024 11:29 am
by RNavega
I'm self taught so my terminology might not be exact, but I think you prefer 'procedural' style -- which is fine by the way. I remember reading some Twitter post saying one of the most complex but elegant programs they had seen was done with just "C and functions and structs".

This seems to go over the differences with OOP some more:
https://blog.intimetec.eu/procedural-vs-object-oriented

I think Lua's tables are more procedural, but anyway OOP has these useful ideas. I'm glad you're gonna make it more modular (especially things that share interfaces, like "Read in key input, change program state"), I'm sure it's gonna help.

Re: Is my approach for keypressed menu suitable.

Posted: Wed Dec 04, 2024 1:31 pm
by MrFariator
originalbluesin wrote: Wed Dec 04, 2024 11:16 am To explain, I don't like to tell a bullet x to move itself at xSpeed. I prefer to tell the bullet manager to move bullet x at xSpeed.
Unsure if you're familiar with the concept, but you should perhaps look into ECS, or data driven programming in general. These are pretty popular concepts in game development circles, though OOP still has its uses.

Re: Is my approach for keypressed menu suitable.

Posted: Thu Dec 05, 2024 5:10 am
by originalbluesin
Thanks for many advice. I'm self taught by reading tutorial, following example. Don't really know the name of concept or terminology that is common. I follow the web link and am sure i like 'procedural' style more than OOP. I read about data driven programming and it look very interesting. I will study it more.

My friend who working in company often sigh whenever he saw my code and said "Well, it's working but ... Whatever float your boat. It's your hobby, just have fun.". He is not try to convince me to use OOP anymore. And i'm stick with this mentality of "Just Having Fun" in programming.

Re: Is my approach for keypressed menu suitable.

Posted: Thu Dec 05, 2024 7:53 am
by MrFariator
The thing about different approaches to structuring your code is that you don't need to exclusively subscribe to OOP, procedural, data driven practices or the like. At the end of the day, they simply describe how to possibly structure your code to tackle specific problems, and each of them can have their own pros and cons. No harm in trying a bit of everything when starting out, since you can learn a bit of something from everything, and apply those learnings in your code.