Class: RGame::Core::App

Inherits:
Object
  • Object
show all
Defined in:
lib/rgame/core/app.rb,
ext/rgame_core/ruby/core_ext.c

Overview

The two things a game needs one of, owned by the app that needs them and built on first use.

Lazily, and that is the point on both counts: an app that draws only primitives builds no asset manager, and one that never plays a sound never opens a device — which is also the right moment to open it, since asking for a sound is the first thing that needs one.

Direct Known Subclasses

Example, Game

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'ext/rgame_core/ruby/core_ext.c', line 111

static VALUE app_initialize(int argc, VALUE *argv, VALUE self) {
    VALUE opts = Qnil;
    rb_scan_args(argc, argv, "0:", &opts); /* no positional args, keywords only */
    if (NIL_P(opts)) {
        rb_raise(rb_eArgError, "missing keywords: :width, :height, :caption");
    }

    const ID keys[4] = { id_width, id_height, id_caption, id_media_root };
    VALUE values[4];
    /* 3 required, 1 optional: raises on a missing or unknown keyword. An absent
     * optional comes back as Qundef. */
    rb_get_kwargs(opts, keys, 3, 1, values);

    rgame_app *app = rgame_app_create(NUM2INT(values[0]), NUM2INT(values[1]),
                                      StringValueCStr(values[2]));
    if (!app) {
        rb_raise(rb_eRuntimeError, "failed to create rgame app");
    }
    /* Store the pointer into the already-wrapped object. */
    RTYPEDDATA_DATA(self) = app;

    /* Where App#assets resolves relative paths from. It means nothing to the C
     * engine — no file is read here — so it is kept as a plain Ruby ivar and
     * lib/rgame/core/app.rb owns everything that reads it. It is a keyword here
     * rather than a writer so that a game says it once, in the same call that
     * makes the window, and cannot set it after an asset has already loaded. */
    rb_iv_set(self, "@media_root",
              values[3] == Qundef ? rb_str_new_cstr("media") : StringValue(values[3]));
    return self;
}

Instance Attribute Details

#media_rootObject (readonly)

Where #assets resolves relative paths from. Set once, as a keyword to the constructor; there is deliberately no writer, because changing it after an asset has loaded would leave a cache keyed against two roots.



79
80
81
# File 'lib/rgame/core/app.rb', line 79

def media_root
  @media_root
end

Instance Method Details

#assetsObject

This app's asset manager, rooted at the media_root: it was made with.

class MyGame < RGame::Core::App
def initialize = super(width: 640, height: 480, caption: 'demo', media_root: MEDIA)
end

app.assets.image('space.png')

A game never constructs one. An Image belongs to one GL context and has to be told which, so something has to hold the app — and since the asset manager is the only thing in the engine that loads from a path, that something is here, once, rather than threaded through every constructor that ends up owning an image.



69
# File 'lib/rgame/core/app.rb', line 69

def assets = @assets ||= AssetManager.new(root: @media_root, app: self)

#audioObject

This app's sound device. Not tied to the window in any way — audio has no GL context and survives one being recreated — it lives here because a game wants exactly one, the same way it wants one asset manager.



74
# File 'lib/rgame/core/app.rb', line 74

def audio = @audio ||= Audio.new

#button_down(button_id) ⇒ Object



430
431
432
433
434
# File 'ext/rgame_core/ruby/core_ext.c', line 430

static VALUE app_default_button(VALUE self, VALUE button_id) {
    (void)self;
    (void)button_id;
    return Qnil;
}

#button_up(button_id) ⇒ Object



430
431
432
433
434
# File 'ext/rgame_core/ruby/core_ext.c', line 430

static VALUE app_default_button(VALUE self, VALUE button_id) {
    (void)self;
    (void)button_id;
    return Qnil;
}

#captionObject



347
348
349
350
# File 'ext/rgame_core/ruby/core_ext.c', line 347

static VALUE app_caption(VALUE self) {
    const char *title = rgame_app_title(rgame_app_unwrap(self));
    return rb_utf8_str_new_cstr(title ? title : "");
}

#caption=(title) ⇒ Object



352
353
354
355
# File 'ext/rgame_core/ruby/core_ext.c', line 352

static VALUE app_set_caption(VALUE self, VALUE title) {
    rgame_app_set_title(rgame_app_unwrap(self), StringValueCStr(title));
    return title;
}

#closeObject



334
335
336
337
# File 'ext/rgame_core/ruby/core_ext.c', line 334

static VALUE app_close(VALUE self) {
    rgame_app_close(rgame_app_unwrap(self));
    return self;
}

#drawObject



419
420
421
422
# File 'ext/rgame_core/ruby/core_ext.c', line 419

static VALUE app_default_draw(VALUE self) {
    (void)self;
    return Qnil;
}

#fpsObject



397
398
399
# File 'ext/rgame_core/ruby/core_ext.c', line 397

static VALUE app_fps(VALUE self) {
    return DBL2NUM(rgame_app_fps(rgame_app_unwrap(self)));
}

#frame_beginObject



408
409
410
411
# File 'ext/rgame_core/ruby/core_ext.c', line 408

static VALUE app_default_frame_begin(VALUE self) {
    (void)self;
    return Qnil;
}

#gamepad_connected(slot) ⇒ Object



443
444
445
446
447
# File 'ext/rgame_core/ruby/core_ext.c', line 443

static VALUE app_default_gamepad(VALUE self, VALUE slot) {
    (void)self;
    (void)slot;
    return Qnil;
}

#gamepad_countObject



389
390
391
# File 'ext/rgame_core/ruby/core_ext.c', line 389

static VALUE app_gamepad_count(VALUE self) {
    return INT2NUM(rgame_app_gamepad_count(rgame_app_unwrap(self)));
}

#gamepad_disconnected(slot) ⇒ Object



443
444
445
446
447
# File 'ext/rgame_core/ruby/core_ext.c', line 443

static VALUE app_default_gamepad(VALUE self, VALUE slot) {
    (void)self;
    (void)slot;
    return Qnil;
}

#gamepad_name(slot) ⇒ Object



384
385
386
387
# File 'ext/rgame_core/ruby/core_ext.c', line 384

static VALUE app_gamepad_name(VALUE self, VALUE slot) {
    const char *name = rgame_app_gamepad_name(rgame_app_unwrap(self), NUM2INT(slot));
    return name ? rb_utf8_str_new_cstr(name) : Qnil;
}

#gamepad_present?(slot) ⇒ Object



379
380
381
# File 'ext/rgame_core/ruby/core_ext.c', line 379

static VALUE app_gamepad_present_p(VALUE self, VALUE slot) {
    return rgame_app_gamepad_connected(rgame_app_unwrap(self), NUM2INT(slot)) ? Qtrue : Qfalse;
}

#heightObject



343
344
345
# File 'ext/rgame_core/ruby/core_ext.c', line 343

static VALUE app_height(VALUE self) {
    return INT2NUM(rgame_app_height(rgame_app_unwrap(self)));
}

#input_axis(device, axis_id) ⇒ Object



369
370
371
# File 'ext/rgame_core/ruby/core_ext.c', line 369

static VALUE app_input_axis(VALUE self, VALUE device, VALUE axis_id) {
    return DBL2NUM(rgame_app_input_axis(rgame_app_unwrap(self), NUM2INT(device), NUM2INT(axis_id)));
}

#input_down?(device, button_id) ⇒ Object



363
364
365
366
367
# File 'ext/rgame_core/ruby/core_ext.c', line 363

static VALUE app_input_down_p(VALUE self, VALUE device, VALUE button_id) {
    return rgame_app_input_down(rgame_app_unwrap(self), NUM2INT(device), NUM2INT(button_id))
               ? Qtrue
               : Qfalse;
}

#needs_redraw?Object



425
426
427
428
# File 'ext/rgame_core/ruby/core_ext.c', line 425

static VALUE app_default_needs_redraw(VALUE self) {
    (void)self;
    return Qtrue;
}

#resize(width, height) ⇒ Object



436
437
438
439
440
441
# File 'ext/rgame_core/ruby/core_ext.c', line 436

static VALUE app_default_resize(VALUE self, VALUE width, VALUE height) {
    (void)self;
    (void)width;
    (void)height;
    return Qnil;
}

#runObject



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'ext/rgame_core/ruby/core_ext.c', line 302

static VALUE app_run(VALUE self) {
    rgame_app *app = rgame_app_unwrap(self);
    run_state rs = { self, app, 0, Qnil };

    rgame_app_callbacks callbacks = {
        .frame_begin = tramp_frame_begin,
        .update = tramp_update,
        .needs_redraw = tramp_needs_redraw,
        .draw = tramp_draw,
        .button_down = tramp_button_down,
        .button_up = tramp_button_up,
        .resize = tramp_resize,
        .gamepad_connected = tramp_gamepad_connected,
        .gamepad_disconnected = tramp_gamepad_disconnected,
        .userdata = &rs,
    };

    rgame_app_run(app, &callbacks);

    /* A callback unwound: the loop has now exited on its own terms, so it is
     * safe to raise. Class, message and backtrace are the original ones. */
    if (rs.failed) {
        rb_exc_raise(rs.exception);
    }

    return self;
}

#ticks_msObject



393
394
395
# File 'ext/rgame_core/ruby/core_ext.c', line 393

static VALUE app_ticks_ms(VALUE self) {
    return UINT2NUM(rgame_app_ticks_ms(rgame_app_unwrap(self)));
}

#update(dt_seconds) ⇒ Object



413
414
415
416
417
# File 'ext/rgame_core/ruby/core_ext.c', line 413

static VALUE app_default_update(VALUE self, VALUE dt_seconds) {
    (void)self;
    (void)dt_seconds;
    return Qnil;
}

#widthObject



339
340
341
# File 'ext/rgame_core/ruby/core_ext.c', line 339

static VALUE app_width(VALUE self) {
    return INT2NUM(rgame_app_width(rgame_app_unwrap(self)));
}