Posted December 09, 2023 by freds72
Demi Daggers was born out of multiple ‘dead ends’:
Note: as usual, knowlege is not lost!
Devil Daggers (short, DD) is a fast paced survival shooter, often seen as an ultra lean Quake. The game is both technically impressive (in its own retro way) and finely designed gameplay wise.
Pico8 is a fantasy console for game development - machine has some hard constraints (cpu, resolution...), yet a very fun platform to work with!
Part of the Quake visual signature, the effect gives a very good sense of movement I decided to keep, regardless of its significant cpu price.
The in-game effect uses a basic screen shearing to mimic rotation:
Initial version used sspr and screen rebase, clocking at 14% - later rewritten using poke4 for half the price!
OG game playground is not square - beyond the visual appeal, it forces the player to watch steps and attempt some daring moves!
How to render it efficiently, without sorting artifacts (inc. when player falls off ground)?
I haven’t spent the last few years in Doom/Quake codebase to miss the answer: a BSP!
Each frame, camera position is pushed down the correct leaf and recursion does the sorting magic:
-- mini bsp: -- 0 -- / \ -- -1 world -- / \ -- brush -2 -- / \ -- brush brush
World smallest BSP recursion:
_bsp[id]=function(cam) -- left/right from closure - BSP tree is generated at runtime local l,r=_bsp[left],_bsp[right] if(cam.origin[plane_id]<=plane) l,r=r,l l(cam) r(cam) end
Second must have effect are shadows.
They ensure player gets a sense of the danger zone when zipping around monsters, and help "ground" monsters in the world.
Shadows started as slanted ellipses based on player view position, rendered using bitplane masking to darken ground.
So far so good except when the approximation doesn’t really work or when the enemy is not visible but shadow should :/
Game was also missing the blood stains unti quite late. This is again a key design element from DD, highlighting your progress in the game.
In one of these ‘I’ll rewrite half of the game on a hunch’ moment, I decided to face one of the dark place of pico8: bitplanes! (and secret spritesheets).
After a bit of head scratching and multiple mini prototypes (bitplanes are tricky) I got the multi-pass rendering right:
First render pass:
Second render pass (‘mode 7’) fixes colors using a "fade to black" palette based on distance (see below) and voilà!
top left: hardware palette/default palette - top right: floor software palette
Note how texture details are kept on both stains (light grey) & shadows (red/green)
For some reason (scoresub being one), I wanted the game to be BBS (or at least) Splore compatible.
I liked the appeal of shipping with the editor I used to craft game models and allow players to make their own multicart game.
The UI went through multiple iterations before settlings on a look&feel closer to the in-game menuing.
Scoresub is a mythical Pico8 API, the one that will project Pico8 into the real "BBS" world.
Alas, 2023 was still not the year of online scores - Thanks Newgrounds to have such nice&free leaderboard support!
As usual, most of the work interfacing with the leaderboard API is in the HTML plate (see plate/fps.html in source code).
That effect swaps between 16x2 palettes for a result I am quite happy with!
Each palette has a ‘moving’ red band that will be sampled based in distance:
With the fade to black, hit, fade to white palettes, game uses a total of 34 palettes!
All palettes are automatically generated using Python, both as a way to facilitate experiments and streamline integration into the carts.
Credits to Krystman on the shmup serie to point out plain white flash is dull, eg make sure hit highlight keeps sprite details.
Long standing complaint on Poom was along the line of:
‘esdf? game sux’ (sic)
I have something to say about games only supporting wasd but 🙄
Back in the Poom days, Zep added that cryptic stat(28), that unfortunately was left unused in final build.
Given the fast pace of Demi Daggers, I really wanted to fix that and came up with what may very well be the first keybinding page for a pico8 game!
Game uses 2d spatial partionning to speed up intersection/collision queries,m. Cell size is 32x32, larger than any enemy yet small enough to not have the whole cast on one cell!
Grid coordinates are packed in a 4 byte value:
z|x>>16 -- y is ‘up’ for various reasons 😜
Enemies can registers into all cells they touch, used for cheap boids behavior and precise bullet/enemy collisions. Makes up for a pretty gif when mapping cell occupancy to color (brighter: more enemies):
Daggers are much faster than monsters and have a zero radius. I resorted to raycasting to ensure perfect collisions while scanning the least amount of cells:
-- scans grid from a to b using normalized vector (u,v) function collect_grid(a,b,u,v,cb) local mapx,mapy=a[1]\32,a[3]\32 -- check first cell (always) -- pack lookup index into a single 16:16 value local dest_idx,map_idx=b[3]\32|b[1]\32>>16,mapy|mapx>>16 cb(_grid[map_idx]) -- early exit if dest_idx==map_idx then return end local ddx,ddy,distx,disty,mapdx,mapdy=abs(1/u),abs(1/v) if u<0 then -- -1>>16 mapdx,distx=0xffff.ffff,(a[1]/32-mapx)*ddx else -- 1>>16 mapdx,distx=0x0.0001,(mapx+1-a[1]/32)*ddx end if v<0 then mapdy,disty=-1,(a[3]/32-mapy)*ddy else mapdy,disty=1,(mapy+1-a[3]/32)*ddy end while dest_idx!=map_idx do if distx<disty then distx+=ddx map_idx+=mapdx else disty+=ddy map_idx+=mapdy end cb(_grid[map_idx]) end end
Mistakes were made...
Hey, game is not "first person perspective" for nothing, let's use real life action!
Needless to say what version made it to the final build :)
DD has a very (very?) particuliar sound design - Demake could not have happened without @ridgekun mad PICO8 sfx&music skills!
Read it all on: https://nerdyteachers.com/PICO-8/Pico-View/?issue=11
Game release went ok, with good reception, but missed most of the social media relay that Poom had (less fame to surf on I guess, Twitter in flame...).
We also went through significant launch tweaks, thanks to players like Werxyz, making the game more approachable but likely too late.
At the time of writing, the game had disapeared from the charts :/ But hey, that's indiedev life!
Another take away is to avoid making games I cannot finish (both original and demake) - it makes some design decisions more random than coming from first hand experience.
---
Thanks Ridgek for the fun!
(and still a big no for “Fighty Fingers”!)
Thanks Heraclum, Miss Mouse & the Pico8 Discord band for being such a nice community!
Source code & tools at: https://github.com/freds72/daggers