Class: SFML::App

Inherits:
Object
  • Object
show all
Extended by:
Keybindings
Defined in:
lib/sfml/app.rb

Overview

Subclass-friendly main loop. Removes the boilerplate of window creation, event pumping, clock management, and clear/display so a small app fits in a few methods.

class MyApp < SFML::App
  antialiasing 4               # class-level config — applies to every instance
  framerate    120
  background   SFML::Color.new(18, 20, 28)

  def setup
    @ball = SFML::CircleShape.new(radius: 30, position: [200, 200],
                                  fill_color: SFML::Color.white)
  end

  def update(dt)
    @ball.move([60 * dt.as_seconds, 30 * dt.as_seconds])
  end

  def draw
    window.draw(@ball)
  end
end

MyApp.new(title: "Hello").run

The loop auto-handles :closed and :key_pressed/:escape by calling #quit; everything else is forwarded to #on_event. Override that to handle keys, mouse, etc.

## Configuration

Every constructor kwarg is also a class-level macro: declare defaults with ‘antialiasing 4` etc. inside the class body, and `MyApp.new` picks them up. Per-instance kwargs still win on a case-by-case basis. Subclasses inherit their parent’s settings — set one in a base class, override in a subclass.

Constant Summary collapse

CONFIG_KEYS =
%i[
  width height title
  framerate vsync background
  style fullscreen
  antialiasing context
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Keybindings

key_handlers, on_key

Constructor Details

#initialize(**opts) ⇒ App

Per-instance kwargs override anything set at the class level. Anything not given here OR at the class level falls back to the hard-coded defaults below.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/sfml/app.rb', line 83

def initialize(**opts)
  cfg = self.class

  width        = opts[:width]        || cfg.width        || 800
  height       = opts[:height]       || cfg.height       || 600
  title        = opts[:title]        || cfg.title        || cfg.name
  framerate    = opts[:framerate]    || cfg.framerate    || 60
  vsync        = opts.fetch(:vsync,        cfg.vsync)
  background   = opts[:background]   || cfg.background   || Color::BLACK
  style        = opts.fetch(:style,        cfg.style)
  fullscreen   = opts.fetch(:fullscreen,   cfg.fullscreen)
  fullscreen   = false if fullscreen.nil?
  antialiasing = opts.fetch(:antialiasing, cfg.antialiasing)
  context      = opts.fetch(:context,      cfg.context)

  window_opts = { framerate: framerate, fullscreen: fullscreen }
  window_opts[:vsync]        = vsync         unless vsync.nil?
  window_opts[:style]        = style         unless style.nil?
  window_opts[:antialiasing] = antialiasing  unless antialiasing.nil?
  window_opts[:context]      = context       unless context.nil?

  @window           = RenderWindow.new(width, height, title, **window_opts)
  @background_color = background
end

Instance Attribute Details

#background_colorObject

Returns the value of attribute background_color.



78
79
80
# File 'lib/sfml/app.rb', line 78

def background_color
  @background_color
end

#current_sceneObject (readonly)

—- Scenes —-

‘current_scene` is whatever the app last activated via `switch_to`. When non-nil, App’s default ‘update` / `draw` / `on_event` / `on_resize` forward to it; key bindings on the scene class shadow ones on the app class while the scene is active.



128
129
130
# File 'lib/sfml/app.rb', line 128

def current_scene
  @current_scene
end

#windowObject (readonly)

Returns the value of attribute window.



77
78
79
# File 'lib/sfml/app.rb', line 77

def window
  @window
end

Class Method Details

.initial_scene(klass = nil) ⇒ Object

Set the scene class the app should switch into automatically at ‘setup` time. Inheritable: a subclass that doesn’t set one inherits the parent’s choice.



70
71
72
73
74
# File 'lib/sfml/app.rb', line 70

def initial_scene(klass = nil)
  @initial_scene = klass if klass
  return @initial_scene if instance_variable_defined?(:@initial_scene)
  superclass.respond_to?(:initial_scene) ? superclass.initial_scene : nil
end

Instance Method Details

#drawObject



192
193
194
# File 'lib/sfml/app.rb', line 192

def draw
  @current_scene&.draw
end

#heightObject



111
# File 'lib/sfml/app.rb', line 111

def height = @window.size.y

#on_event(event) ⇒ Object

The framework consumes:

* `:closed` — closes the window (always)
* `:resized` — forwarded to `on_resize`
* `:key_pressed` whose code matches a scene- or app-level
  `on_key` binding

Everything else lands here. Override to handle game-specific input. By default forwards to the current scene’s ‘on_event`.



203
204
205
# File 'lib/sfml/app.rb', line 203

def on_event(event)
  @current_scene&.on_event(event)
end

#on_resize(width, height) ⇒ Object

Default: forward to the current scene. Override to additionally do app-wide layout fixups; call ‘super` to keep the scene in the loop.



210
211
212
# File 'lib/sfml/app.rb', line 210

def on_resize(width, height)
  @current_scene&.on_resize(width, height)
end

#pauseObject

—- Pause / resume —-

While paused, ‘update(dt)` is not called — the world stops advancing. `draw` keeps running so the window still updates (handy for pause overlays that need to be drawn over a frozen scene).



153
# File 'lib/sfml/app.rb', line 153

def pause   = (@paused = true)

#paused?Boolean

Returns:

  • (Boolean)


156
# File 'lib/sfml/app.rb', line 156

def paused? = @paused == true

#quitObject

Close the window. The main loop exits at the start of the next frame.



115
116
117
118
# File 'lib/sfml/app.rb', line 115

def quit
  @window.close
  self
end

#resumeObject



154
# File 'lib/sfml/app.rb', line 154

def resume  = (@paused = false)

#runObject

The main entry point. Calls #setup once, then runs the per-frame loop until the window closes.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/sfml/app.rb', line 160

def run
  setup
  clock = Clock.new
  while @window.open?
    dt = clock.restart
    @window.each_event { |event| _dispatch(event) }
    update(dt) unless paused?
    @window.clear(@background_color)
    draw
    @window.display
  end
  teardown
  self
end

#setupObject

—- Override these in subclasses ————————————–

Defaults forward to the current scene when one is active. The ‘initial_scene` class macro auto-instantiates a scene at `setup` time so subclasses with `initial_scene Foo` don’t have to define ‘setup` themselves.



182
183
184
185
186
# File 'lib/sfml/app.rb', line 182

def setup
  if (klass = self.class.initial_scene)
    switch_to(klass)
  end
end

#switch_to(scene_or_class) ⇒ Object

Activate ‘scene_or_class`. Tears down the previous scene before calling `setup` on the new one. Accepts either:

* a Scene subclass — instantiated with `self` as host
* an existing Scene instance — used as-is


134
135
136
137
138
139
140
141
142
143
144
# File 'lib/sfml/app.rb', line 134

def switch_to(scene_or_class)
  new_scene =
    case scene_or_class
    when Class then scene_or_class.new(self)
    else            scene_or_class
    end
  @current_scene&.teardown
  @current_scene = new_scene
  @current_scene&.setup
  @current_scene
end

#teardownObject

Called once after the main loop exits. Tears down the active scene by default.



216
217
218
# File 'lib/sfml/app.rb', line 216

def teardown
  @current_scene&.teardown
end

#toggle_pauseObject



155
# File 'lib/sfml/app.rb', line 155

def toggle_pause = (@paused = !paused?)

#update(dt) ⇒ Object



188
189
190
# File 'lib/sfml/app.rb', line 188

def update(dt)
  @current_scene&.update(dt)
end

#widthObject

Width and height shortcuts that always reflect the current window size — matters once the user is allowed to resize.



110
# File 'lib/sfml/app.rb', line 110

def width  = @window.size.x