DuckBlur - A fast-paced platformer (in development)

The water effect is really cool! It might look better a little stronger as you say--but it already shows up nicely on static objects like the seaweed.

1 Like

Looks fantastic! I have been longing for a fun platformer for the playdate, this looks like just that :smiley:

1 Like

Definitely buying this when it's polished and completed. Love the coin bomb!


Thanks! The game is still nowhere near completion but I am making progress.

Here is the new demo :playdate:. It is still possible to crank up or down the frame rate.
The overworld is made of 4 maps. I hope that loadings are not too noticeable when switching from map to map.

EDIT: it seems that there was some files missing, here is a new PDX. (222.0 KB)


That's looking really nice! I'm curious, are you using the sprite/collision system from the SDK at all, or did you hand-roll your own? I've been thinking of making a small platform and the sprite system seems almost too constraining for things like tiled map behind the sprites, etc

1 Like

I'm using the sprite system from the SDK but not its collision system. It seems to be nice and fast but it only checks for rectangles and I wanted to have slopes and loops. I also don't see how you can use it with tilemaps, so I wrote my own system instead.

For tilemaps, I am associating each tile with two mathematical functions. It makes the player movement smoother than checking collision with the image mask:

I then check the functions of the tile located under/over the player to know where he should land after a jump.

And between sprites I am using hitboxes, like the SDK:


Ooh, the idea of using mathematical functions for tiles' collision boundaries is fascinating! Thanks for all the tips!

Since you use sprites -- how do you draw the background/tile map? So far in my experiments I've found that if the display list is enabled, then anything I draw that isn't a sprite is discarded.

Yes, the screen is cleared when you call playdate->sprite->updateAndDrawSprites() so I create one sprite per layer. You have to set the sprite bounds to the size of the screen and make the sprite dirty when the camera moves.

The "Sprite Game" example from the SDK more or less covers this. Search for the Background Sprite parts in PlaydateSDK/C_API/Examples/Sprite Game/game.c.

Creating a sprite per layer also allow fun stuff like changing the player layer on the fly simply by changing the zIndex:


I've not long had my device and decided to give your last build you posted here today. Solid 50fps for me on device and nice to know that fast smooth gameplay is achievable. I'm just now implementing tilemap rendering with scrolling for a fast paced belt scroller beat em up (streets of rage etc). Now I know what is possible in terms of performance at least with only a few enemies on screen it gives me a bit more confidence that what I want to do should be possible.

1 Like

Yes the Playdate is a very capable game console. I was targeting a lower framerate at first but it seems possible to make a fast platformer/action game with a solid 49 fps.

I really love belt-scroll action games as well so I am looking forward to your game :+1:

This is very nicely done! How are you getting the running up ramp? I think most collision detection are in squares, but are you moving the player.y based on slope increase?

1 Like

Thanks! And yes, when the player is on the ground, I am using the mathematical functions from the tile below the player to set his y location (or x if running vertically).

I generate function tables from my level editor:

float paletteChateauXHitbox(uint16_t tile, **float** x) {
    switch (tile) {
    case 114:
        return 32 - x / 2;
    case 216:
        return x * x / 256;
    case 217:
        return (x + 32) * (x + 32) / 256;
    // […]
        return 32;

And call it to obtain the bottom y coordinate. I then calculate the center point from the player current angle.

static void followGround(Player * _Nonnull self) {
    // […]
    MELIntPoint pixel = MELLayerPointInTileAtPoint(self->super.ground, bottomCenter);
    // Calculate y coordinate from x
    float groundY = ground->parent->xHitbox(tile, pixel.x);

    bottomCenter.y = MELLayerTileTop(ground, bottomCenter) + groundY;
    const float angleToHead = self->angle - MELDirectionValues[self->super.direction] * MEL_PI_2;
    self->super.frame.origin = MELRectangleOriginIsCenterGetPointAtAngle(MELRectangleMakeWithOriginAndSize(bottomCenter, self->super.frame.size), angleToHead);



I'm unable to get either build to run on my Playdate. It says it's not compatible with my system version. (I'm up to date) I'm not sure why, but either way, I'm excited to see this when it's completed. Excellent work.

1 Like

The game needs to be rebuilt for the 2.0 SDK, that's why it isn't booting


Yes, it is a C game and it needs to be recompiled to run on firmware 2+. I received my device 2 weeks ago and encountered several issues/crashes that I need to fix.

However level creation was very slow with my current editors, and it started to feel like a burden. So I started a smaller project named Kuroobi to maintain my motivation. I hope to release Kuroobi by the end of the year. I will continue DuckBlur afterward.


I hope you get your motivation for this game back at the time you intend to continue to develop it. You're making the first real platformer that I have seen on this device. It is currently untestable on this forum, but from the pictures the only thing I might mention is that the ground could use some consistent texture to help the eyes grow more familiar with the terrain.


it wont work with new software please update love the game!

I also have to fix bugs I found when I got my playdate. It's more than just recompiling the game for the new OS :disappointed:.

But I will do my best! Thank you for your support!