fuzzix.org

Jaws was never my scene and I don't like Star Wars
Subscribe

Perl 5 and qqw{} - Quote word with interpolation

Perl 5 has these handy quote operators to save you time, like q{} and qq{} to quote strings without and with interpolation, e.g.


0> q{"I don't have to escape quotes!"}
$res[1] = ""I don't have to escape quotes!""

It also has qw{} to create lists from quoted words. This does not interpolate variables:


1> my $message = "This is the commit message"
$res[1] = "This is the commit message"
2> qw{ git commit -m $message }
$res[2] = [
    [0] "git",
    [1] "commit",
    [2] "-m",
    [3] "$message"
]

So what if I want interpolation in my quoted word operator? qqw{}, right? Well no, but Perl 6 has qqw{}:


> my $message = "This is the commit message"
This is the commit message
> join "\n", qqw{ git commit -m $message }
git
commit
-m
This
is
the
commit
message

Hmmm, not quite what I need. Looks like what I want is qqww{}


> join "\n", qqww{ git commit -m "$message" }
git
commit
-m
This is the commit message

Perfect! Except I'm working with Perl 5. Enter Quote::Code which offers code interpolation. Variables are code, so:


3> use Quote::Code
4> qcw{ git commit -m {$message} }
$res[2] = [
    [0] "git",
    [1] "commit",
    [2] "-m",
    [3] "This is the commit message"
]

Bingo.

Shout out to freenode #perl regulars pink_mist for mentioning Quote::Code and mauke for building it in the first place!

by fuzzix on Fri, 16 Nov 2018 09:08. Comment.

DBIx::Class::Schema::Versioned errors

Quick post, since it might help someone - I couldn't find a complete solution on the intertubes...

DBIx::Class::Schema::Versioned makes use of SQL::Translator to generate DDL and diffs.

My own use-case calls for SQLite in some test/dev environments and PostgreSQL in staging/production, but I was unable to generate diffs for a recent change:

translate: Error with parser 'SQL::Translator::Parser::SQLite': Unable to parse line ...

Postgres has neat types like timestamp with time zone which you should think about using. SQLite, on the other hand, treats types a little differently.

No problem, you say, SQLite can simply ignore this type!

Fine in theory, but SQL::Translator::Parser::SQLite's grammar is a little tighter. The solution is to use the single-word definition for the desired type, 'timezonetz'.

Thanks to ilmari and others in #dbix-class for helping me get this straight!

by fuzzix on Wed, 08 Mar 2017 14:03. Comment.

Gaming On Windows

Here's a quick one to dust off the cobwebs and purge my system of these poisonous humours.

While running Steam on Slackware I was always keenly aware of those "Not Available on Your Platform" dialogs, their barbs sinking deep into my envy gland. "I really should get me a Windows license." I thought - "The green pastures of carefree gaming over there on Windows will be mine. It will all Just Work, Seamlessly."

Ha!

GFWL Bullshit

Games For Windows Live is an abortive PC gaming platform conceived by Microsoft to make Windows gaming more ... Xbox-y. Many games require it before allowing you to even play. Standard evil DRM. I mistakenly thought using Steam would limit me to one evil DRM platform.

As the GFWL Service no longer exists, this essentially requires you have an Xbox live account... and your games now complain if you haven't upgraded to "Gold" or whatever their "Fuck you, pay me" tier is called.

I've had a lot of fun with GFWL. Here's the sequence of events as I recall them.

Shite! To get my save games back I had to install this Definitely-not-dodgy Fallout 3 GFWL patch.

XInput Bullshit

You remember PC Gaming, right? Any old crappy USB joypad works.

Well, not any more. There's an API called XInput which is designed to work exclusively with Xbox 360 compatible controllers. Yep, you all have to buy new controllers or, alternatively, get an advanced degree in DLL-fuckology.

But not every game is on board with this XInput bullshit. Want your expensive 360 sticks to work with games made before, say, 2006? Or ones that simply don't support Xinput? Tough shit.

This XInput / DirectInput thing is such a cluster fuck that there's a whole market of Game controllers with a switch to select which standard you need per-game

What's that? This rant belongs in 2007? Hey fuck you, I'm old. It takes me a long time to figure out the arbitrary anti-engineering decisions of fucked up commercial entities.

by fuzzix on Thu, 10 Dec 2015 14:10. Comment.

Back from the Dead - Amstrad NC100

Good guy on the Internet Ken Guest gave me a NC100 many years ago. I since broke it by plugging in the wrong power supply by accident (Sinclair and Amstrad gear from the era usually used a negative centre power supply connector where most everyone else did the opposite).

This results in a popped surface mount fuse. There is a very naughty fix described here where you just solder a strand of wire (fuse wire if you can be bothered) across the contacts.

Because I am an oaf with clown hands and sausage fingers, I managed the soldering job but broke the LCD ribbon cable when reassembling it. Luckily I found a Replacement ribbon cable from Farnell which is now installed and working:

Photo of working NC100
NC100 - back in "action".

I picked up a spare ribbon cable in case I want to open the thing up again and put in, you know, a real fuse. The cables I got are at least 5cm longer than the original, so future maintenance work should be a little easier. Contacts for the CR2032 lithium cell also got an alcohol scrub while I was in there (if you don't have this, the unit refuses to turn on).

by fuzzix on Tue, 12 Aug 2014 11:30. Comment.

The Joy in What We Do

So Sawyer X gave this talk at this year's YAPC::NA, I urge you to watch it even if you're not into Perl.

Sawyer X - The Joy in What We Do

I have nothing to say beyond this, other than the fact that I love writing software so much that I hate software. If you ever want to see a fat guy dancing (sober), then come on over to my house when it looks like the software I'm writing is going well. If you want to see me spit blood and rage, all you have to do is tell me about some software that's only kind of crappy. I'm not a great programmer, barely a good one, but when I know it can trivially be done better and isn't, I get angry. This means I am angry with myself a lot. Anyone who knows me will know the phrase "software sucks".

So, on to Perl. These people are doing it better, but nobody seems to know about it. This is a major part of what Sawyer's talk above is about (seriously, watch it). So, I've appeared out of my hole (yeah, been quiet around here, got me a job) to briefly list some Perl awesomeness, the bits I like and some bits I anticipate using, in no particular order:

The MOP - So Stevan's crazy and wants to put the ideas behind Moose right into core Perl. This is cool for a number of reasons, not least of which (to me) are performance and packaging. Having that power right off the bat (even in your crappy system Perl) will be great.

Dancer (2) - Don't use it as an example of how to write code, but this blog was written using Dancer 2 and it was so easy to put together that there should be a law against it. I am going to update my templates later to crow about this in the footer or something. It's great, try it. Try Dancer 2.

SDL - Perl's SDL bindings have gotten a lot of love in recent years and are really excellent. (SDLx::App makes things so ridiculously easy that you have no excuse. This very site has a few posts on how simple it is. Trust me, if it wasn't simple, I wouldn't be able to do it.) Maybe we can make some more great games other than Frozen Bubble.

FFI::Raw - Because XS is scary and writing bindings needs to be easier. Graham Ollis gave a talk about it this year.

Subroutine Signatures - "Does perl have subroutine signatures yet?" "YES! (Provisionally, pending successful experimentation)". Honestly, I don't know what this is and have never needed it ;-) ...Perl's implementation goes way beyond the capabilities of most other languages, see Ricardo's 5.20 talk linked below.

Perlbrew - I need this to live. Just don't do the curl-pipe-bash thing, I hate that. Do use perlbrew, though.

Data::Printer - stop using Data::Dumper for debugging right now.

Irssi - Irssi has a Perl interface (and an AnyEvent adaptor) which means I can easily write bots and scripts to do useful (but mostly annoying) things in Perl.

Fuse - Got something which you could abstract as a filesystem? Do it in Perl. I've yet to use this in anger, but I think it's because I lack the imagination to come up with something powerful enough to require it. I wrote some toy examples to demonstrate its usefulness and accessibility on blogs.perl.org.

Modern Perl - chromatic is crazy and wrote a great book and gives it away for free (though you should buy it... every year). It's a really short, accessible set of useful practices which will enhance the way you think about programming and Perl.

And while you're here (if indeed you are still here), Ricardo Signes gave two great, funny talks this year about new and cool things in Perl:

Ricardo Signes (rjbs) - 1.21 Gigawatts

Ricardo Signes (rjbs) - Perl 5.20: Perl 5 at 20

There was also this:

Charlie Stross - Keynote

Wow.

by fuzzix on Thu, 26 Jun 2014 16:39. Comment.

Repliconz - dev log 2, Collisions Revisited, Sound

In part 1 we got some basic collision detection by checking for overlapping rectangles in each move call.

Using positions of Things at a given frame for collision detection presents a problem. If an object is small and fast moving, like a bullet, it will have fairly long vectors to travel and can appear to pass through objects if the delay between frames is long enough.

Collision (or miss) illustration
The arrows coming from our things in Frame 1 (roughly) represent the vector they'll be travelling along. Between frames the bullet passes through the baddie but since collision detection occurs as "snapshots" of each frame, the baddie escapes, then mocks us.

What we need is something that will calculate if two objects of given dimensions travelling along a given vector over a given time-frame will collide. As usual, CPAN provides. Collision::2D is a module which offers us this kind of continuous collision detection.

So what has changed? Well, we now keep track of the previous move to pass to Collision::2D::dynamic_collision as we also want to perform and show the move (to avoid dodgy looking collision detection where things look like they hit when they merely get close). Game::Repliconz::Thing now has a move method which stores the parameters of the move for later calculation, then applies it to x and y (performs the move).

sub move {
    my ($self) = @_;
    $self->last_x = $self->x;
    $self->last_y = $self->y;
    $self->last_dt = $self->dt;
    $self->last_v_x = $self->v_x;
    $self->last_v_y = $self->v_y;
    $self->x += $self->v_x * $self->dt;
    $self->y += $self->v_y * $self->dt;
}

The collision detection itself is now fairly simple:

sub collision {
    my ( $self, $thing ) = @_;
    return 0 if (!$self->alive || !$thing->alive);
    my @rect = map { hash2rect( {
        x => $_->last_x,
        y => $_->last_y,
        h => $_->h,
        w => $_->w,
        xv => $_->last_v_x,
        yv => $_->last_v_y,
    } ) } ( $self, $thing );
    return dynamic_collision ($rect[0], $rect[1], interval => $self->last_dt);
}

sub check_collides_with {
    my ( $self, $things ) = @_;
    my $check_distance = 50;

    for my $thing (@{$things}) {
        next if (abs($self->x - $thing->x > $check_distance) ||
                 abs($self->y - $thing->y > $check_distance));
        if ($self->collision( $thing )) {
            $thing->hit;
            $self->hit;
            return 1;
        }
    }
}

The collision function generates a pair of rectangles (rects) for use by Collision::2D. We then check if a collision occurred at any time between the last frame and the current one with dynamic_collision.

We can then use check_collides_with on our Things, pass in another set of Things and find out if there was a hit. It only performs the expensive detection if the objects in question are within a certain distance

So in our move handler callback in Game::Repliconz we simply:

$self->{hero}->check_collides_with($self->{baddies});

for my $bullet (@{$self->{hero}->{bullets}}) {
    $bullet->check_collides_with($self->{baddies});
}

Then we can filter the lists of Things (bullets, baddies, whatever) by their alive method which simply checks remaining lives - bullets have "lives" to end their path when they hit an enemy. You could make stronger bullets as a bonus by modifying this value, of course. So how does this all look?

Youtube link

Note: there was a small bug present in the code this video was captured from, so the collision might not be 100% represented here, but it's close enough.

If you watch this video, you might notice another addition - sound.

This is done pretty simply. There has been code present since the beginning to initialise the SDL audio subsystem and load up a few samples:

sub _init_audio {
    my ( $opts ) = @_;

    SDL::init(SDL_INIT_AUDIO);

    if ( SDL::Mixer::open_audio( 44100, SDL::Constants::AUDIO_S16, 2, 4096) == 0 ) {
        $opts->{audio} = 1;
    }
    else {
        $opts->{audio} = 0;
        carp "Audio disabled : " . SDL::get_error();
        return 0;
    }

    SDL::Mixer::Channels::allocate_channels(4);
    @{$opts->{samples}}{ qw/
        bonus_sweeps
        laser
        explosion
    / } = (
        SDL::Mixer::Samples::load_WAV("$opts->{working_dir}/sound/bonus_sweeps.wav"),
        SDL::Mixer::Samples::load_WAV("$opts->{working_dir}/sound/laser.wav"),
        SDL::Mixer::Samples::load_WAV("$opts->{working_dir}/sound/explosion.wav"),
    )
}

This allocates four channels, which we can use for each event type we want to make noise for, shooting, explosions, bonuses and so on. Using a single channel means sounds won't "overlap", so for lasers and such it gives a classic "pew-pew-pewww" effect rather than an echoey/overlaid one. Hope I got my onomatopoeia right there.

The sample, channel and audio subsystem status are passed into the given Thing on instantiation.

$self->{hero} = Game::Repliconz::Guy->new( {
    field_width  => $self->{w},
    field_height => $self->{h},
    shoot_noise  => $self->{samples}->{laser},
    shoot_channel => 1,
    audio => $self->{audio},
} );

...which then plays the sound when the given event occurs:

sub shoot {
    ...
    SDL::Mixer::Channels::play_channel( $self->{shoot_channel}, $self->{shoot_noise}, 0 ) if $self->{audio};
    ...
}

The samples themselves were generated by sfxr, an excellent tool to generate old school FX for games. Each sample was created by hitting the "Randomize" button until a sound I liked played, though there is plenty of scope for controlling and fine tuning the sounds yourself.

That's it for now. As always, comments, criticism and contributions welcome.

Code for Repliconz is on Github.

by fuzzix on Tue, 28 Jan 2014 19:30. Comment.

Repliconz - dev log 1, Enemies, Collision Detection

So what's changed since part 0?

Youtube linky, Repliconz gameplay so far

The green things chasing the Guy are instances of the Baddie class. Since Baddies are Things, many methods have been moved from Guy to Game::Repliconz::Thing which are common to Baddies, including move, constrain_velocity_xy and shoot. Bullets are also Things, so now technically bullets can shoot bullets, but we won't be doing that... yet. Anyone like the idea of cluster bullets?

The movement of baddies is somewhere between a bullet and the guy. A vector is calculated from current baddie position and guy position, so the baddies pursue the guy. Converting this to a unit vector using the bullet's Pythagoras square method would allow the baddies to travel in any arbitrary direction. This results in them converging rather quickly, giving the appearance of a single, super-strong enemy.

It turns out that constrain_velocity_xy, which was created to limit the guy's speed, can also be used to limit the baddies' movement to 45 degree angles. They eventually converge using this method too, but hopefully you'll have shot a good few of them by the time that happens. Anyway, now Game::Repliconz::Baddie::move is pretty simple:

sub move {
    my ( $self, $target_x, $target_y, $dt, $app ) = @_;

    my $v_y = $target_y - $self->{y};
    my $v_x = $target_x - $self->{x};

    ($v_x, $v_y) = $self->constrain_velocity_xy($v_x, $v_y);

    $self->{x} += $v_x * $self->{velocity} * $dt;
    $self->{y} += $v_y * $self->{velocity} * $dt;

    ($self->{x} > 0 || $self->{x} < ($app->w - $self->{w})) &&
    ($self->{y} > 0 || $self->{y} < ($app->h - $self->{h})) &&
    ($self->{on_screen} = 1);
}

The on_screen property will be used later in decisions about collision detection, since bullets continue off screen and enemies spawn there.

Baddies are pushed onto a queue in the main move callback, with more being spawned when the queue goes below a certain size.

Our collision detection is currently very simple, using Collision::Util to check overlapping rectangles. I need to check out how accurate this is, as the framerate / app delay will have an effect... a bullet might be rendered on either side of a target without ever "passing through" it. The video above appears to show some baddies being "hit", yet surviving.

The last change you might have noticed is the cursor is now a sprite, which is achieved simply by hiding the cursor and rendering a png file sprite at the mouse X/Y position.

As always, comments, criticism and contributions welcome.

Code for Repliconz is on Github.

by fuzzix on Mon, 27 Jan 2014 00:27. Comment.

Repliconz - dev log 0

So I decided to play around with Perl's SDL bindings. A game appears to be happening, so let's play around with this dev log idea too, documenting the process and pitfalls of making some stuff move around on screen.

It turns out you don't need to know a whole lot to make this happen, in the simplest cases at least. Let's see what we have so far:

Youtube linky

OK, so we're not going to set the world alight just yet. Anyway, we have a guy and some bullets. We are missing enemies, scoring, action, pew pew noises and any incentive to play. These come later, I hope.

Controls are somewhere between Robotron 2084 or Smash TV (both made by the same legendary game developer) and Abuse. WASD / Arrows move the guy, aim and shoot with the mouse.

Perl's SDL distribution comes with a convenient extension called SDLx::App which is built on SDLx::Controller. This makes creating game loops simple. You set a delay between iterations, handler callbacks for the things a game loop usually contains (input events, drawing and so on) and then run it. Each callback gets a delta-T value (the time that has passed since the previous iteration) so you can tie this value to your physics model and get consistent movement even if you are occasionally starved of resources or change your frame rate (delay).

The three classes of handler we need to define are event, move and show. For those of you familiar with MVC style frameworks, these handlers are (very) roughly analogous: Model ~= move, View ~= show, Controller ~= event.

It should be noted that any number of callbacks for each handler type can be added, though this code currently has just one for each.

Event processing is triggered on input, mouse movements, container changes and so on. Our callback function receives an SDL::Event instance containing the queued events which need handling. Let's take a look at the event handler in Repliconz:

sub events {
    my ( $self, $event, $app ) = @_;
    return $app->stop if $event->type == SDL_QUIT;

    if ($event->type == SDL_KEYDOWN) {
        $self->{keys}->{$event->key_sym} = 1;
    }
    if ($event->type == SDL_KEYUP) {
        $self->{keys}->{$event->key_sym} = 0;
    }

    if ($event->type == SDL_MOUSEMOTION) {
        $self->{mouse}->{x} = $event->motion_x;
        $self->{mouse}->{y} = $event->motion_y;
    }

    if ($event->type == SDL_MOUSEBUTTONDOWN && $event->button_button == SDL_BUTTON_LEFT) {
            $self->{mouse}->{firing} = 1;
    }
    if ($event->type == SDL_MOUSEBUTTONUP && $event->button_button == SDL_BUTTON_LEFT) {
            $self->{mouse}->{firing} = 0;
    }
}

Simple enough, we store the key up/down states in $self->{keys} and mouse position / button state in $self->{mouse}. So what do we now that we know what our player is doing? We move...

How does our move handler look?

sub move {
    my ( $self, $dt, $app, $t ) = @_;
    my $v_x = 0;
    my $v_y = 0;
    my $bomb = 0;

    for (grep { $self->{keys}->{$_} } keys %{$self->{keys}}) {
        when ($self->{controls}->{keyboard}->{u}) { $v_y += -1 }
        when ($self->{controls}->{keyboard}->{d}) { $v_y += 1 }
        when ($self->{controls}->{keyboard}->{l}) { $v_x += -1 }
        when ($self->{controls}->{keyboard}->{r}) { $v_x += 1 }
        when ($self->{controls}->{keyboard}->{b}) { $bomb = 1 }
    }

    $self->{hero}->move( $v_x, $v_y, $dt, $app );

    $self->{hero}->self_destruct if ($bomb);

    $self->{hero}->shoot( $dt, $self->{mouse}->{x}, $self->{mouse}->{y} ) if ($self->{mouse}->{firing});

    for my $bullet (@{$self->{hero}->{bullets}}) { $bullet->move( $dt, $app ) }
}

There's a little more going on here. First we go through the keys which were set in the event handler and add 1 (since we end up manipulating this unit vector with an actual velocity later) to X/Y velocity for each control. Simply setting them to 1/-1 means that the hero will still move when opposite controls are being pressed, so we add them to prevent this: 1 and -1 being added will cancel each other out. Ignore the $bomb stuff, that's in the "notion" stage of development.

$self->{hero} is an instance of Game::Repliconz::Guy. Let's take a look at its move method:

sub move {
    my ( $self, $v_x, $v_y, $dt, $app ) = @_;

    ($v_x, $v_y) = constrain_velocity_xy($v_x, $v_y);

    $self->{x} += $v_x * $self->{velocity} * $dt;
    $self->{y} += $v_y * $self->{velocity} * $dt;

    ($self->{x} < 0) && ($self->{x} = 0);
    ($self->{x} > ($app->w - $self->{w})) && ($self->{x} = $app->w - $self->{w});
    ($self->{y} < 0) && ($self->{y} = 0);
    ($self->{y} > ($app->h - $self->{h})) && ($self->{y} = $app->h - $self->{h});
}

The moving itself is taken care of by setting our new coords to our unit vector coords multiplied by our velocity and delta-T, the time that has passed. This is the key to getting consistent movement even if timing / framerate changes. You can check this out by reducing the delay in app setup to effectively increase framerate. You can also manipulate $dt in your callbacks to speed up or slow down the action. For example, to add a slo-mo mode you could divide $dt by 2.

The last four lines reset our position if the guy tries to move outside the bounds of the play field. What is constrain_velocity_xy up to?

sub constrain_velocity_xy {
    my ( $v_x, $v_y ) = @_;

    ($v_y > 0) && ($v_y = 1);
    ($v_y < 0) && ($v_y = -1);
    ($v_x > 0) && ($v_x = 1);
    ($v_x < 0) && ($v_x = -1);

    # Moving diagonally, moderate eventual velocity
    if ( $v_y != 0 && $v_x != 0 ) {
        $v_y *= 0.707; # sin(45 degrees)
        $v_x *= 0.707;
    }

    return ($v_x, $v_y);
}

There are a couple of things going on here. The first is we reset the bounds of our unit vector set in the move handler callback. Since there are two sets of controls and we used an 'additive' approach to setting the movement, if you press two buttons for the same direction, you end up with twice the required vector length and, eventually, twice the velocity. If any direction is set, we reset it to 1 or -1.

The other thing is, if we are moving diagonally, our vector is no longer length 1, it's the length required to go from corner to corner of a square with sides of length 1. Pythagoras tells us this is ~1.41. To correct this and set our vector length back to 1, we reduce the coords of our vector end point to ~0.7 (sine of 45°) of their original value. This diagram on Mathematics For Blondes illustrates quite effectively why this works, I think.

If you watched the video above, you saw that the guy wasn't the only thing moving. We also have bullets. Bullets are instances of Game::Repliconz::Bullet, which are pushed in and out of a queue (currently arbitrarily limited to 20 items) in our instance of Game::Repliconz::Guy when shoot is triggered by the fire button:

sub shoot {
    my ( $self, $dt, $mouse_x, $mouse_y ) = @_;
    state $total_dt = 0;
    $total_dt += $dt;
    return unless ($total_dt >= $self->{cooling_time});
    $total_dt -= $self->{cooling_time};

    push @{$self->{bullets}}, Game::Repliconz::Bullet->new( {
        guy => $self,
        target_x => $mouse_x,
        target_y => $mouse_y
    });

    shift @{$self->{bullets}} if (scalar @{$self->{bullets}} > $self->{max_bullets});
}

We track time elapsed by adding delta-T values until our gun cooling time is reached. This allows us to easily control rate-of-fire at any stage, to implement a high ROF bonus by just reducing the cooling time, for example.

We tell the bullet about the guy and mouse position, but how do bullets aim in the mouse cursor direction? Another unit vector is created on instantiation, based on the guy's position and the mouse cursor position:

sub new {
    my ( $class, $opts ) = @_;

    $opts->{x} = $opts->{guy}->{x} + ($opts->{guy}->{w} / 2);
    $opts->{y} = $opts->{guy}->{y} + ($opts->{guy}->{h} / 2);
    $opts->{w} = 4;
    $opts->{h} = 4;

    # normalise vector : guy position -> target position
    $opts->{v_y} = $opts->{target_y} - $opts->{y};
    $opts->{v_x} = $opts->{target_x} - $opts->{x};
    my $v_len = sqrt($opts->{v_y} ** 2 + $opts->{v_x} ** 2);
    $opts->{v_y} /= $v_len;
    $opts->{v_x} /= $v_len;

    $opts->{colour} = 0xFFFFFFFF;
    $opts->{velocity} = 70;

    bless $opts, $class;
}

So we set the initial x/y of our bullet to be the centre of the guy, along with width and height for drawing later. We then make use of Pythagoras' theorem again to convert the difference between the start point and mouse position into a unit vector. If you consider the X and Y axes of our coords to be the sides of a right triangle, adding their squares will give us the length of the vector guy -> cursor. We just need to divide our coords by this value to wind up with a vector of length 1.

Once you have all this information, the move method becomes very simple:

sub move {
    my ( $self, $dt, $app ) = @_;

    $self->{x} += $self->{v_x} * $self->{velocity} * $dt;
    $self->{y} += $self->{v_y} * $self->{velocity} * $dt;
}

Now that we know where everything is (or is going to be), the next step is drawing. Our show handler is pretty naive, simply blanking the screen and calling draw for each of our objects:

sub show {
    my ( $self, $dt, $app ) = @_;
    SDL::Video::fill_rect( $app, SDL::Rect->new(0, 0, $app->w, $app->h), 0 );

    $self->{hero}->draw($app);
    for my $bullet (@{$self->{hero}->{bullets}}) { $bullet->draw($app) }

    $app->update();
}

The draw method is inherited from Game::Repliconz::Thing:

sub draw {
    my ( $self, $app ) = @_;
    $app->draw_rect( [ $self->{x}, $self->{y}, $self->{w}, $self->{h} ], $self->{colour} );
}

We simply draw a rect at x, y, of size w, h of the given colour for each instance directly onto a passed SDLx::App instance.

Doesn't get simpler than that, I think. I hope you found this interesting and/or useful. Comments, criticism and contributions welcome. You can check out the code for Repliconz on Github.

The Wolfire Games Blog features a couple of cool posts on linear algebra with a focus specifically on game development:

Linear algebra for game developers ~ part 1

Linear algebra for game developers ~ part 2

by fuzzix on Fri, 24 Jan 2014 19:21. Comment.

FreeBSD, fail2ban and newsyslog

Just a short note on FreeBSD's newsyslog and fail2ban, since I couldn't find this information on the interwebtubes.

Fail2ban does not respond to HUP/USR1 (or any other) signal to notify config or log changes. This sort of thing is achieved using fail2ban-client.

Newsyslog is geared towards the sending of signals, since that's what a *nix daemon would traditionally expect. Recently patched and working in newsyslog is the R flag, which allows you to provide a path to some executable instead of a PID file. Config for fail2ban in /etc/newsyslog.conf now looks like this:

# logfilename         mode count size when  flags [/pid_file] [sig_num]
/var/log/fail2ban.log 600  5     500  $W0D5 JR    /usr/local/bin/fail2ban-logrotate.sh

Contents of /usr/local/bin/fail2ban-logrotate.sh are simply:

#!/bin/sh

/usr/local/bin/fail2ban-client set logtarget /var/log/fail2ban.log >/dev/null

Something doesn't sit right about this approach but it appears to do the business. Better ideas welcome in the comments.

OpenBSD's newsyslog appears to allow any free form command as long as it's wrapped in double quotes. Handy.

by fuzzix on Mon, 13 Jan 2014 13:45. Comment.

Dancer2::Plugin::UnicodeNormalize

After reading about chromatic's Mojolicious::Plugin::UnicodeNormalize I thought "I could have a crack at that" and created a similar thing for my own framework of choice, Dancer2. Having never written any code that might possibly be of any utility to another human being, I have never uploaded anything to CPAN. Anyway, now there's Dancer2::Plugin::UnicodeNormalize.

Though that's not as interesting as the cool things about uploading to CPAN. The first cool thing is how simple it is with Dist::Zilla which pretty much takes care of a whole load of stuff you used to have to do by hand. It has a plugin system to do neat things like Git integration (version tagging, changelog generation and such). I wish a similar thing existed for autotools (maybe it does) but the last thing that project needs is more scripts.

The second cool thing is CPAN Testers. Anyone familiar with Perl will probably know about this already, but I think it warrants mentioning at every possible opportunity... the family finds it a drag at Christmas dinner, but I will not stop. CPAN Testers is an integration framework in which a team of excellent volunteers will build and test your code on a dizzying variety of platforms. I don't have any MacOS or Windows installs here, but I know my code builds, runs and passes all tests on these platforms.

If only you could find this out before releasing the distribution... Oh, you can. -TRIAL releases are not indexed for general release on CPAN (they show up as developer releases), though they are tested by the CPAN Testers. Releasing a trial with Dist::Zilla is as simple as dzil release --trial. Give it a couple of days and you'll have access to dozens (or even hundreds) of verbose test logs. Failing test results are mailed to you. It's a bit like Travis CI and the like, but for releases. And it requires no action on your part, you just get it for free, whether you like it or not. But you'll like it.

Criticise or contribute to Dancer2::Plugin::UnicodeNormalize on Github. This post is longer than the code.

by fuzzix on Fri, 29 Nov 2013 13:44. Comment.

< Older