Starting a game through a GUI Menu?

So I’ve decided to go ahead and try and implement a menu in my game; the game is working perfectly fine, and the menu I have, though somewhat incomplete, is also working perfectly fine. However, I can’t seem to get the two to mesh together properly.

When I start the game without a menu, I create an instance of a World() class, which in turn creates an instance of a Player() class and instances of several NPC classes, while the Player() class creates instances of bullet classes. I’ve tried implementing it such that the menu in the menu.py file creates a world instance within menu.py, but the methods in the game.py classes don’t recognize the world instance anymore. If I create the world instance within the game.py and run() it, the game starts automatically, ignoring the menu; the same goes if I don’t run() world in game.py, but only in a function of menu.py.

This is my menu code, without any implementation of the game itself, which is in the SSTE file I import at the start:

import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectGui import *
from pandac.PandaModules import TransparencyAttrib
from direct.task import Task
from direct.fsm import FSM
import sys, os
#import SSTE

def generateWorld():
    menu.destroy()

class mainMenu(DirectObject):
    
    def __init__(self):
        wp = WindowProperties()
        wp.setTitle("Menu Testing")
        wp.setSize(800, 600)

        self.mainFrame = DirectFrame(frameColor=(0, 0, 0, 1),
                      frameSize=(-2, 2, -2, 2),
                      pos=(0, 0, 0))
     
        self.background = OnscreenImage(image = 'MenuBackground.png', pos = (0, 0, 0), scale = (0.3, 0.5, 0.8))
        self.background.setTransparency(TransparencyAttrib.MAlpha)
        self.background.reparentTo(self.mainFrame)

        self.buttonMaps = loader.loadModel("mainButton_maps")

        self.startButton = DirectButton(text = "Start", text_scale=(0.07, 0.07), relief=None, geom= (self.buttonMaps.find("**/button_ready"),
                                                         self.buttonMaps.find("**/button_click"),
                                                         self.buttonMaps.find("**/button_rollover"),
                                                         self.buttonMaps.find("**/button_disabled")), command=generateWorld, pos=(-1, 0, 0.6))
        self.startButton.reparentTo(self.mainFrame)

        self.instructionsButton = DirectButton(text = "Instructions", text_scale=(0.07, 0.07), relief=None, geom= (self.buttonMaps.find("**/button_ready"),
                                                         self.buttonMaps.find("**/button_click"),
                                                         self.buttonMaps.find("**/button_rollover"),
                                                         self.buttonMaps.find("**/button_disabled")), command=self.seeInstructions, pos=(-1, 0, 0.2))
        self.instructionsButton.reparentTo(self.mainFrame)
        
        self.optionsButton = DirectButton(text = "Options", text_scale=(0.07, 0.07), relief=None, geom= (self.buttonMaps.find("**/button_ready"),
                                                         self.buttonMaps.find("**/button_click"),
                                                         self.buttonMaps.find("**/button_rollover"),
                                                         self.buttonMaps.find("**/button_disabled")), command=sys.exit, pos=(-1, 0, -0.2))
        self.optionsButton.reparentTo(self.mainFrame)

        self.exitButton = DirectButton(text = "Exit", text_scale=(0.07, 0.07), relief=None, geom= (self.buttonMaps.find("**/button_ready"),
                                                         self.buttonMaps.find("**/button_click"),
                                                         self.buttonMaps.find("**/button_rollover"),
                                                         self.buttonMaps.find("**/button_disabled")), command=sys.exit, pos=(-1, 0, -0.6))
        self.exitButton.reparentTo(self.mainFrame)
        
        self.instructionsFrame = DirectFrame(frameColor=(0, 0, 0, 1),
                      frameSize=(-2, 2, -2, 2),
                      pos=(0, 0, 0))
        self.instructionsFrame.hide()

        self.instrTitle = DirectLabel(text="Keyboard Controls:", text_scale=(0.1, 0.1), relief=None, text_fg=(255, 255, 255, 100), pos=(-0.9, 0, 0.9))
        self.instrTitle.reparentTo(self.instructionsFrame)

        self.gameInstructions1 = DirectLabel(text="WASD - Character control", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-0.8, 0, 0.8))
        self.gameInstructions1.reparentTo(self.instructionsFrame)
        self.gameInstructions2 = DirectLabel(text="Up & Down Arrows - Look up & down", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-0.595, 0, 0.7))
        self.gameInstructions2.reparentTo(self.instructionsFrame)
        self.gameInstructions3 = DirectLabel(text="E - Jump", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-1.1, 0, 0.6))
        self.gameInstructions3.reparentTo(self.instructionsFrame)
        self.gameInstructions4 = DirectLabel(text="Space Bar - Shoot", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-0.945, 0, 0.5))
        self.gameInstructions4.reparentTo(self.instructionsFrame)
        self.gameInstructions5 = DirectLabel(text="Q - Switch weapons", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-0.905, 0, 0.4))
        self.gameInstructions5.reparentTo(self.instructionsFrame)
        self.gameInstructions6 = DirectLabel(text="Escape - Exit", text_scale=(0.08, 0.08), relief=None, text_fg=(255, 255, 255, 100), pos=(-1.03, 0, 0.3))
        self.gameInstructions6.reparentTo(self.instructionsFrame)

        self.backFromInstr = DirectButton(text = "Back", text_scale=(0.07, 0.07), relief=None, geom= (self.buttonMaps.find("**/button_ready"),
                                                         self.buttonMaps.find("**/button_click"),
                                                         self.buttonMaps.find("**/button_rollover"),
                                                         self.buttonMaps.find("**/button_disabled")), command=self.seeMainMenu, extraArgs=[self.instructionsFrame], pos=(1, 0, -0.8))
        self.backFromInstr.reparentTo(self.instructionsFrame)

        self.accept("escape", sys.exit)
        
    def destroy(self):
        self.mainFrame.destroy()

    def seeInstructions(self):
        self.mainFrame.hide()
        self.instructionsFrame.show()

    def seeMainMenu(self, fromFrame):
        fromFrame.hide()
        self.mainFrame.show()

    

menu = mainMenu()
run()

My plan was to make something like this:

def generateWorld():
    menu.destroy()
    SSTE.world.run()

But this doesn’t work, nor does world = SSTE.World() in the generateWorld function. Any tips?

Maybe (in menu.py) :
import game
game.world = game.World()
run()

First off: keep it simple and implement your stuff step for step:

  1. Try to make a very minimal, standalone graphical menu without any interactive parts/assigned functions
  2. Set up a FSM with menu as a state so that you can turn it on and off
  3. Assign keystrokes to try out the FSM. E.g. space turns menu off, enter turns it on
  4. Make a second state for the FSM that loads a test scene and call it “game” or “world” or something.
  5. Assign self.request(“World”) or similar to your menu buttons.

If you mix this all it’s really hard to find a bug in the end.

BTW, what you do wrong is working in global namespace. Create a new class that instances mainMenu and which has a method generateWorld().
Only thing you should instance from within global namespace (the level without indentation) is your game control class that manages everything by itself.

To fix your code quick and (very) dirty: add ‘global menu’ as the first line of generateWorld().