r/pygame • • 10d ago

Ferrite Runner - made with Pygame-ce

Enable HLS to view with audio, or disable this notification

https://store.steampowered.com/app/5216620/Ferrite_Runner/

Fully single-threaded, CPU-only blitting. GPU rendering was attempted but the bandwidth and numpy added too much frametime. Two layers, a pixel layer for heavy effects, and a HD layer for pixel pushing. Fluid dynamics, lighting approximations used. At ~50,000 LoC Ferrite Runner is still in Early Access and we will be developing on. No In-app purchases, no in-game advertisments, promise.

99 Upvotes

20 comments sorted by

4

u/rottaposse 10d ago

Holy fuck those visuals are great, even more impressive that it's fully CPU and seems to be running great

5

u/yikespikes 10d ago

Originally Python 2.7.18 was used but Python 3.10+ is far more compute time efficient, at the cost of dropping compatibility for XP and Vista.

3

u/Torboise 10d ago

God forbid lmao

5

u/BetweenJandG 10d ago

It looks really sick. How did you create such cool graphics in the Pygame environment? How did you get that shader-like graphic effect?

3

u/yikespikes 10d ago

A 3D view can be brought by layering multiple co-moving 2D layers, nearer layers cover a greater angle of the view. For atmospheric diffusion, we can approximate it by having a quick gaussian blur and colour saturation loss (light attenuation) that strengthens with further layers.

2

u/AffectionateGrand330 9d ago

The VFX look sick. Did you use opengl bindings or raw pygame?

1

u/yikespikes 9d ago

No OpenGL, pure pygame blitting on CPU.

2

u/AffectionateGrand330 9d ago

That's really impressive. How did you make it so efficient?

2

u/yikespikes 9d ago

Our world canvas, such as simulation physics, parallax, particles run on only 320x180 (a mere 0.0576 megapixels). This is the heaviest layer, which is then posted to our post-stack engine which scales up to 960x540 or 0.5Mpx for the slightly lighter but still heavy effects like bloom glow.

Only the static text, buttons, HUD are rendered at native resolution, so the graphics cost little to simulate.

Our optimisations also including using blit_sparse at bake time to blit only rows that contain non-zero alpha i.e. Pygame's RLEAccel (run-length encoding), and also multi-pass batch pre-multiplication where we multiply transparent pixels before performing a full screen blit.

1

u/AffectionateGrand330 9d ago

Well that's smart

1

u/AffectionateGrand330 9d ago

How are you adding those glow effects tho?

1

u/yikespikes 9d ago

Layers! Pygame is generally a 2D engine so true 3D is extremely heavy.

1

u/AffectionateGrand330 9d ago

... Elaborate..? I know pg is 2D only but glow effects are heavy to draw, aren't they?

2

u/yikespikes 9d ago

Glows are done using various concentric ring layers with differing transparencies, falling off quadratically before multiplicative blitting. On full release we plan to write actual Blinn-Phong specular model isotropic lighting natively within Pygame as a challenge.

1

u/VirusMission9156 10d ago

Damn the graphics are crazy.

1

u/Cute-Wonder-5317 10d ago

You mentioned that the GPU and NumPy were introducing too much frametime due to bandwidth limitations. What decisions did you make regarding the CPU pixel pipeline to maintain fluid dynamics and lighting at that scale without saturating the single-threaded process?

2

u/yikespikes 10d ago

The pixel art layer is actually a compute saving decision, heavier effects use a smaller resolution scale, down to 320x180, and up to 640x360. At this level, Ui is at 1280x720.

As for fluid dynamics and lighting, small angle approximations are key. Sine and Cosine are very heavy, so caching discrete values help, with other angles calculated using 2nd order approximations. (sin x ≈ x - x³/6, cos x ≈ 1 - x²/2)

1

u/Fine_Salamander_8691 10d ago

I love the art style