pods::...roller(3pm)

๐Ÿ“ฆ version 2.548, in opensuse-leap 16.0

okay bestie, welcome to the pods::...roller(3pm) page from the User Contributed Perl Documentation. This is your cheat sheet for SDLx::Controller, and itโ€™s lowkey iconic. ๐Ÿซถ

โœจ NAME โœจ

SDLx::Controller โ€” handles the loops for events, movement and rendering. Thatโ€™s the whole vibe. ๐Ÿ’…

๐Ÿท๏ธ CATEGORY ๐Ÿท๏ธ

Extension, Controller.

๐ŸŒธ SYNOPSIS ๐ŸŒธ

use SDLx::Controller;

# create our controller object
my $app = SDLx::Controller->new;

# we could also do:
my $app = SDLx::App->new;
# because App is also a controller

# register some callbacks
$app->add_event_handler( \&on_event );
$app->add_move_handler( \&on_move );
$app->add_show_handler( \&on_show );

# run our game loop
$app->run;

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

The core of an SDL application/game is the main loop, where you handle events and display your elements on the screen until something signals the end of the program. This usually goes in the form of:

while (1) {
    ...
}

The problem most developers face, besides the repetitive work, is to ensure the screen update is independent of the frame rate. Otherwise, your game will run at different speeds on different machines and this is never good (old MS-DOS games, anyone?). ๐Ÿ˜ญ

One way to circumvent this is by capping the frame rate so it's the same no matter what, but this is not the right way to do it as it penalizes better hardware.

This module provides an industry-proven standard for frame independent movement. It calls the movement handlers based on time (hi-res seconds) rather than frame rate. You can add/remove handlers and control your main loop with ease. โœจ

๐Ÿงš METHODS ๐Ÿงš

๐ŸŒŸ new

SDLx::Controller->new(
    dt    => 0.5,
    min_t => 0,
    event => $event_object,
);
  • dt โ€” specifies the length, in seconds, of a full movement step, and defaults to 0.1. dt can be anything and the game can still look the same. It only makes a difference when you change dt without changing all the things in the movement step that are being multiplied by the first move argument. If you lower the dt, everything will move faster than it did with it set higher, and vice-versa. This is useful to add slo-mo and fast-forward features to the game โ€” all you would have to do is change the dt. ๐ŸŒ€
  • min_t โ€” specifies the minimum time, in seconds, that has to accumulate before any move or show handlers are called, and defaults to 1 / 60. Having the min_t at 1 / 60 ensures that the controller can update the screen at a maximum of 60 times per second. A "V-Sync" such as this is necessary to prevent video "tear", which occurs when the app is updating faster than the monitor can display. Setting it to 0, as seen above, will let the app run as fast as it possibly can. ๐Ÿ”ฅ
  • delay โ€” specifies a loop delay in millisecs to place on the controller loop. NOTE: picking a good delay based on the needs can help reduce CPU load and pressure. ๐Ÿ˜ฎโ€๐Ÿ’จ
  • event โ€” is a SDL::Event object that events going to the event callbacks are polled in to. It defaults to SDL::Event->new(). ๐ŸŽ€

All parameters are optional.

Returns the new object.

๐Ÿƒ run

After creating and setting up your handlers (see below), call this method to activate the main loop. The main loop will run until stop is called.

All hooked functions will be called during the main loop, in this order:

  1. Events
  2. Movements
  3. Displaying

Please refer to each handler below for information on received arguments. Note that the second argument every callback receives is the SDLx::Controller object.

๐Ÿ›‘ stop

Returns from the run loop.

โธ๏ธ pause

Attempts to pause the application with a call to SDL::Events::wait_event. See SDL::Events.

Takes 1 argument which is a callback. The application waits for the next event with wait_event. When one is received, it is passed to the callback as the first argument, along with the SDLx::Controller object as the second argument. If the callback then returns a true value, pause will return. If the callback returns a false value, pause will repeat the process.

This can be used to easily implement a pause when the app loses focus:

sub window {
    my ($e, $app) = @_;
    if($e->type == SDL_QUIT) {
        $app->stop;
        # quit handling is here so that the app
        # can be stopped while paused
    }
    elsif($e->type == SDL_ACTIVEEVENT) {
        if($e->active_state & SDL_APPINPUTFOCUS) {
            if($e->active_gain) {
                return 1;
            }
            else {
                $app->pause(\&window);
                # recursive, but only once since the window
                # can't lose focus again without gaining it first
            }
        }
    }
    return 0;
}
โš ๏ธ Note: if you implement your own pause function, remember to update current_time to the current time when the application unpauses. This should be done with Time::HiRes::time. Otherwise, time will accumulate while the application is paused, and many movement steps will be called all at once when it unpauses. Note 2: a pause will be potentially dangerous to the run cycle (even if you implement your own) unless called by an event callback.

๐Ÿฃ paused

Returns 1 if the app is paused, undef otherwise. This is only useful when used within code that will be run by pause:

sub pause {
    # press P to toggle pause

    my ($e, $app) = @_;
    if($e->type == SDL_QUIT) {
        $app->stop;
        # quit handling is here so that the app
        # can be stopped while paused
    }
    elsif($e->type == SDL_KEYDOWN) {
        if($e->key_sym == SDLK_p) {
            # We're paused, so end pause
            return 1 if $app->paused;

            # We're not paused, so pause
            $app->pause(\&pause);
        }
    }
    return 0;
}

๐ŸŽŸ๏ธ add_event_handler

Register a callback to handle events. You can add as many subs as you need. Whenever a SDL::Event occurs, all registered callbacks will be triggered in order. Returns the order queue number of the added callback.

The first argument passed to registered callbacks is the SDL::Event object. The second is the SDLx::Controller object.

sub stop {
   my ($event, $app) = @_;
   if($event->type == SDL_QUIT) {
       $app->stop;
   }
}
$app->add_event_handler(\&stop);

๐Ÿ•น๏ธ add_move_handler

Register a callback to update your objects. You can add as many subs as you need. Returns the order queue number of the added callback.

All registered callbacks will be triggered in order for as many dt as have happened between calls, and once more for any remaining time less than dt. The first argument passed to the callbacks is the portion of the step, which will be 1 for a full step, and less than 1 for a partial step. Movement values should be multiplied by this value. The full steps correspond to the amount of dt passed between calls, and the partial step corresponds to the call with the remaining time less than dt. The argument can be 0 if no time has passed since the last cycle. If you need to protect against this, set a min_t, or put a return unless $_[0] at the start of every move handler.

The second argument passed to the callbacks is the SDLx::Controller object. The third is the total amount of time passed since the call of run.

You should use these handlers to update your in-game objects, check collisions, etc. so you can check and/or update it as necessary.

sub move_ball {
    my ($step, $app, $t) = @_;
    $ball->move_x( $ball->x_vel * $step );
    $ball->move_y( $ball->y_vel * $step );
}

๐ŸŽจ add_show_handler

Register a callback to render objects. You can add as many subs as you need. Returns the order queue number of the added callback. All registered callbacks will be triggered in order, once per run of the run loop.

The first argument passed is the time, in seconds, since the previous call. The second is the SDLx::Controller object.

sub show_ball {
    my ($delta, $app) = @_;
    $app->draw_rect(
        [ $ball->x, $ball->y, $ball->size, $ball->size ],
        $ball->colour
    );
}

๐Ÿงน remove_move_handler( $index ), remove_event_handler( $index ), remove_show_handler( $index )

Removes the handler with the given index from the respective calling queue.

You can also pass a coderef. The first coderef in the handler list that this matches will be removed.

Returns the removed handler.

๐Ÿงผ remove_all_move_handlers, remove_all_event_handlers, remove_all_show_handlers

Removes all handlers from the respective calling queue.

๐Ÿšฟ remove_all_handlers

Quick access to removing all handlers at once.

โฑ๏ธ dt, min_t, current_time

If an argument is passed, modifies the corresponding value to the argument. dt and min_t will keep their old value until the beginning of the next run cycle.

Returns the corresponding value.

๐Ÿ‘‘ AUTHORS ๐Ÿ‘‘

See AUTHORS in SDL.

๐ŸŒˆ ACKNOWLEDGEMENTS ๐ŸŒˆ

The idea and base for this module comes from Lazy Foo's Frame Independent Movement http://www.lazyfoo.net/SDL_tutorials/lesson32/index.php tutorial, and Glenn Fiedler's Fix Your Timestep http://gafferongames.com/game-physics/fix-your-timestep/ article on timing.


And thatโ€™s the whole page, bestie. This one is from perl v5.42.0, dated 2024-05-01. Go make something frame-independent and cute ๐Ÿซถ๐Ÿ’–

๐Ÿ“ฆ where this page comes from