module Ray # Games are used to manage different scenes. They also init Ray by creating a # window. # # == Creating a Game # There are several ways of doing this. # Using a block: # Ray::Game.new("my game") do # ... # end # # Using the instance directly: # game = Ray::Game.new("my game") # ... # game.run # # Subclassing: # class Game < Ray::Game # def initialize # super("my game") # ... # end # end # # Game.new.run # # == Registring scenes to a Game # Games need the scenes they use to be registered. The most obvious way to # do it is to use scene with a block: # scene :game do # # See Ray::Scene # end # # You may also call it to register a subclass of Ray::Scene # scene(:game, GameScene) # Which is the same as: # GameScene.bind(self) # Assuming GameScene's scene_name is set to :game # # == Managing the scene stack # You can push a scene to the game: # push_scene :game # When #run will be called, it will show the scene :game. Notice that, if you # push more than one scene, only the last one will be seen directly. However, # if you remove it later, the previous scene will be shown. # # You can thus also remove a scene from your stack: # pop_scene # Removes the last scene # # exit is not exactly the same: it will ask the scene to quit before doing this. # exit! will do something totally different: completely kill the game. # # == Handling events # Games can listen to events just like scenes. Since the event runner will change # often, it needs to register every time it changes it. You can pass a block to # the register method: # register do # on :some_event do some_stuff end # end # # You may also want to override register in suclasses: # def register # on :some_event do some_stuff end # end # class Game include Ray::Helper # Creates a new game. # # @yield If a block is given, it is instance evaluated. #run will then be # called. # # @option opts [Ray::Window] :window (nil) If set, this window will be used # instead of creating a new one # @option opts [Ray::Vector2, #to_vector2] :size ([640, 480]) Size of the # window. # @option opts [true, false] :fullscreen (false) True to make the window # fullscreen. # @option opts [true, false] :resizable (false) True to make the window # resizable. # @option opts [true, false] :no_frame (false) True to create a window # with no decorations. def initialize(title, opts = {}, &block) @game_registered_scenes = {} @game_scenes = SceneList.new(self) @game_register_block = nil @game_exited = false @game_title = title create_event_runner defaults = {:size => [640, 480]} options = defaults.merge(opts) unless options[:window] size = options[:size].to_vector2 @game_window = Ray::Window.new @game_window.open(title, size, options) else @game_window = opts[:window] end if block instance_eval(&block) run end end # Adds a scene to the stack using its name. # # You must call Game#scene before this. If you subclassed scene, # then call bind to register it: # scene :something, SomeClass # SomeClass.bind(self) # # @param [Symbol] scene_name The name of the scene which should be pushed # @param *args Arguments passed to the scene def push_scene(scene_name, *args) @game_scenes.push(scene_name, *args) end # Pops the last scene. def pop_scene @game_scenes.pop end # Registers a new scene with a given name. the block will be passed # to klass.new. # # @param [Symbol] name the name of the new scene # @param [Class] klass the class of the scene. def scene(name, klass = Scene, &block) scene = @game_registered_scenes[name] = klass.new(&block) scene.game = self scene.event_runner = event_runner scene.window = @game_window end # @return [Ray::Scene] scene register for a given name def registered_scene(name) @game_registered_scenes[name] end # Runs the game until the last scene gets popped. # Will call Ray.stop. def run while running? event_runner.clear register @game_scenes.run_current end end # @param [true, false] True if the scene list isn't empty # and the user hasn't exited from the game. def running? !@game_exited && !@game_scenes.empty? end # Registers a block to listen to events # Subclasses can also overrid this method to register for events. def register(&block) if block_given? @game_register_block = block else @game_register_block.call if @game_register_block end end # Removes the current scene of this game def exit @game_scenes.exit_current end # Kills the game, removing all the scenes it contains. def exit! @game_exited = true @game_scenes.exit_current @game_scenes.clear end def title @game_title end def scenes @game_scenes end def window @game_window end def scenes=(list) @game_scenes = list unless running? @game_scenes.exit_current end end def event_runner=(runner) super @game_registered_scenes.each do |name, scene| scene.event_runner = runner end end def inspect "game(#{title.inspect})" end end # (see Ray::Game#initialize) def game(title, opts = {}, &block) Ray::Game.new(title, opts, &block) end module_function :game end