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



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
141
142
143
144
# File 'ext/rgame_core/ruby/core_ext.c', line 115

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.



81
82
83
# File 'lib/rgame/core/app.rb', line 81

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.



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

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.



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

def audio = @audio ||= Audio.new

#button_down(button_id) ⇒ Object



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

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

#button_up(button_id) ⇒ Object



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

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

#captionObject



356
357
358
359
# File 'ext/rgame_core/ruby/core_ext.c', line 356

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



361
362
363
364
# File 'ext/rgame_core/ruby/core_ext.c', line 361

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

#closeObject



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

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

#drawObject



428
429
430
431
# File 'ext/rgame_core/ruby/core_ext.c', line 428

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

#fpsObject



406
407
408
# File 'ext/rgame_core/ruby/core_ext.c', line 406

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

#frame_beginObject



417
418
419
420
# File 'ext/rgame_core/ruby/core_ext.c', line 417

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

#frame_endObject



433
434
435
436
# File 'ext/rgame_core/ruby/core_ext.c', line 433

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

#gamepad_connected(slot) ⇒ Object



457
458
459
460
461
# File 'ext/rgame_core/ruby/core_ext.c', line 457

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

#gamepad_countObject



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

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

#gamepad_disconnected(slot) ⇒ Object



457
458
459
460
461
# File 'ext/rgame_core/ruby/core_ext.c', line 457

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

#gamepad_name(slot) ⇒ Object



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

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



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

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

#heightObject



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

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

#input_axis(device, axis_id) ⇒ Object



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

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



372
373
374
375
376
# File 'ext/rgame_core/ruby/core_ext.c', line 372

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



439
440
441
442
# File 'ext/rgame_core/ruby/core_ext.c', line 439

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

#resize(width, height) ⇒ Object



450
451
452
453
454
455
# File 'ext/rgame_core/ruby/core_ext.c', line 450

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

#runObject



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'ext/rgame_core/ruby/core_ext.c', line 310

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,
        .frame_end = tramp_frame_end,
        .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



402
403
404
# File 'ext/rgame_core/ruby/core_ext.c', line 402

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

#update(dt_seconds) ⇒ Object



422
423
424
425
426
# File 'ext/rgame_core/ruby/core_ext.c', line 422

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

#widthObject



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

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