SPATTRIS (#MWDJ25 entry) mechanic breakdown

Hey! Long time follower, first time poster. :waving_hand:

Recently submitted my first solo entry for the #MadeWithDefold Jam 2025 (Thanks @Pawel for organizing it, had lots of fun!) and a few people asked how the mechanic was made. Since the forum proved to be such a tremendous source of knowledge for me, I figured I might as well share my process here for anyone else who might find it interesting.

The Game

For a while I had this idea of a Tetris-like game where instead of the usual tiles, players need to stack random physics-enabled blobs of color and create clusters that span across the board. This year’s theme “Twist on a Classic” was the perfect excuse to try that mechanic and see how it goes.

:joystick: Check out SPATTRIS by leshido

Development

Technically, I guess there are 2 aspects to this merging mechanic that are worth explaining: the visual “blobby” shader, and the physics based logic. There’s also the GameBoy postprocess effect applied which might not be directly related to the mechanic but I think helps tie everything together nicely.

So, let’s delve in.

Physics

Each blob the game drops is actually made of 4 separate “ball” game objects. The balls have 2 collision objects, one of type Dynamic to control their movement and collisions, and one slightly bigger of type Trigger used in the linking process. Each trigger is assigned a certain physics group and mask, corresponding to the different blob colors. Once a trigger event fires, I create_joint on the 2 colliding objects to constrain their movements with a “rope”, essentially gluing them together. I also keep track of these links in a table and use it later to check each resulting cluster to see if it touches both walls.

Visuals

The blobbing effect involves a custom render script and materials. Each ball is assigned a sprite with a radial gradient in one of the three RGB primary colors. The sprites are configured to use the “screen” blending mode and a material which for the most part is an exact copy of the builtin sprite material, except it uses a different tag. Instead of the default “tile” rendering predicate, they’ll be rendered at an earlier pass to an intermediate render target. I won’t bother with the details on how to achieve this, but I can point to the Post Processing example project which for me was a very clear and concise introduction to rendering targets. The relevant code in the update() function looks like this:

-- Draw blobs to another canvas
render.enable_state(graphics.STATE_BLEND)
render.set_render_target(self["rt_blobs"])
render.clear({[render.BUFFER_COLOR_BIT] = vmath.vector4(0, 0, 0, 0)})
render.draw(predicates["blob"])
render.set_render_target(self["rt_world"])
render.disable_state(graphics.STATE_BLEND)

NOTE: “rt_blobs” and “rt_world” are render targets created in the init() phase

Example of using a step function (in Affinity Photo)

With the balls all rendered to an off-screen texture, a model is used to actually display them. This is handled by the model’s material, in which a glsgl step() function is used to limit the values of the gradients and create the signature metaball/blobby effect. Because the balls can only ever be either Red, Green or Blue, the step function essentially joins them in 3 distinct groups. Something like this:

mediump vec4 tex_sample = texture(texture_sampler, var_texcoord0.xy);
mediump vec4 stepped_sample = step(0.5, tex_sample);
out_fragColor = stepped_sample;

Finally, I took @MasterMind’s Gradient Map sample project as reference and modified it to be used as a full screen postprocess effect. Combining it with a stepped 4 color “gradient” instantly made everything feel more gameboy-y. It also allowed me to work simply in greyscale for the entire game’s assets, knowing the coloring will be handled by the shader.

The 4-color "gradient" used in the effect

Conclusion

That’s it! Thank you for reading, I hope this was a helpful breakdown. It was my first time using Defold for anything more than simple prototypes and I learned a lot doing so. Highly recommend giving the Jam a try next year :smiley:

13 Likes

Thank you for sharing! Great idea for a game and great “feel” :heart:

Do you think the speed of the falling blocks can be reduced at the begining so that players have more control over where the blobs land?

1 Like

I’m sure it can! I have a few improvements in mind, hoping to release a post-jam version sooner than later :sweat_smile:

1 Like

Thanks for sharing! Very fun to play :grinning_face_with_smiling_eyes: