u/Final_Huckleberry408

Image 1 — I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io
Image 2 — I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io
Image 3 — I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io
Image 4 — I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io
Image 5 — I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io
▲ 3 r/jogoscoop+1 crossposts

I built an HD-2D beat 'em up using Godot 4 and a Python/AI asset pipeline — just dropped a free demo on itch.io

Hey guys,

Wanted to share a milestone on a project I’ve been building: GRAYFALL, an arcade beat 'em up in Godot 4 combining 2D pixel art with 3D lighting (normal maps on 2D sprites, volumetric fog, dynamic shadows).

Rather than just generating one-off images, I set up a custom Python pipeline to handle sprite sheets, normal maps for 3D lighting, and procedural audio synthesis.

Just uploaded a quick demo on itch.io to test the combat feel and performance:

  • 5 distinct character archetypes (sword/shield, brute, rogue, mage, support)
  • Stage 1 + Boss fight (Arcade mode)
  • Training dojo + local co-op up to 6 players

Would love some honest feedback from other devs on the game feel, hit feedback, and visual cohesion.

Link: https://guilhermeandradejid-eng.itch.io/grayfall-demo

u/Final_Huckleberry408 — 4 days ago

How we animate GRAYFALL: the sprites are Python, not drawings

Every character in GRAYFALL is generated by a Python renderer: a pose is a dict of joint angles, shapes are rasterized at 4x, cel-shaded, then mode-downsampled into crisp pixels. Two GIFs go with the post.

We did not pick a style by arguing about it

https://i.redd.it/ob7hn81qjwth1.gif

The sprites we had were weak: flat faces, robotic animation, no identity. Instead of debating references, I had four complete renderers written from scratch, each one rendering the same character (Lyra, the archmage) with the same animations — idle, walk, run, a three-hit combo, a cast and a hurt — plus a portrait, a key-art card and a notes file arguing its own pros and cons.

Candidate Proportions Height What it cost us
HD-2D 16-bit (Octopath-like) 1:4 ~50 px Too small to read 6 players on screen
Chibi (Young Souls-like) 1:2.5 ~60 px Cute, but no reach in attack poses
Painted (Odin Sphere-like) 1:6 ~100 px Gorgeous, far too expensive per frame
Arcade hi-bit (KOF / Metal Slug) 1:4.6 ~80 px Shipped

The lab was the cheapest decision of the project. Four throwaway renderers cost less than one wrong style applied to twenty characters. The output of the losing three is still in the repo — nothing was deleted, and the comparison still settles arguments months later.

How one frame is actually made

https://i.redd.it/0au72usujwth1.gif

No drawing program is involved anywhere in this chain. Python, Pillow and numpy only.

  1. A pose is a dict of angles. lean, twist, shoulder angle, grip position along the staff. An IK solver places elbows and knees from the hand and foot targets, so I animate intent, not limbs.
  2. Shapes are rasterized at 4x. Capsules, ribbons and Catmull-Rom splines write into three buffers at supersample resolution: material, tone and part id.
  3. Cel shading is a mask offset. Each material has 5 tones. Shift the part's mask toward one fixed light and the uncovered band becomes the next tone down. A second, longer shift gives the deep tone; shifting the other way carves the highlight. Parts also cast onto what sits behind them.
  4. Downsample by majority vote. Each 4x4 block takes the most common (part, material, tone) key. No averaging, no anti-aliasing, so the result is a real pixel image and not a shrunk render.
  5. Internal lines. A dark seam is written only where two different parts touch — arm over torso, not arm over arm.
  6. One pixel of black outline around the body, FX excluded. That outline is what makes the character read at 640x360 over a lit 3D stage.
  7. The face is painted after downsampling, pixel by pixel, at final resolution. Eyes and mouth are 1-2 px features; if they go through the 4x4 vote they turn to mush. This is the single biggest difference between "hi-bit" and "doll".
  8. Normal maps are generated, not painted, and the 3D stage lights the 2D sprite with them.
  9. Hair and cloth are springs. Chains with rest angle, stiffness, gravity, damping and wind, simulated per frame. Secondary motion is never keyed by hand — it falls out of the sim, which is why it never looks pasted on.
  10. Key frames and sub-frames are different things. The hitboxes fire on key frames (the logical frames the game reasons about). Sub-frames are inserted purely for smoothness and the game logic never sees them.

Why code, and the contract that makes it safe

Hand-drawn frames are a dead end at this scale: 20 playable heroes, ~50 animations each, 6 players on screen. The win is not speed, it is propagation. Change the shading rule and every frame of every character gets it. Redesign a hero's coat and the 50 animations come back dressed.

The danger is obvious: regenerate the art and you break every hitbox. So each character has a frozen contract — a JSON snapshot of animation names, the number of logical frames per animation, and the tick budget of each frame. The renderer may change poses, add sub-frames, redraw the whole costume. It may not change the contract. Gameplay keeps indexing frame 3 of punch2 and lands the same hit it always did.

Every frame also exports anchors: hand, hand direction, off-hand, head, chest, weapon tip. Trails, sparks, projectiles and held props attach to those anchors, so VFX never drift out of the hand when the animation is redrawn.

The gate: a script that tells me a character is not done

Quality went up when "looks good to me" stopped being the standard. qa_hero.py <id> prints one row per hero and fails it against fixed thresholds.

Metric Minimum Why it exists
animations 50 a character missing getup, dizzy or carry feels broken, not stylish
logical frames 195 the floor under all 50
fluidity (sub-frames per logical frame) 0.85 this is the number that reads as "smooth"
frames per attack 5 anticipation, contact, follow-through, recovery — three poses is not an attack
one frame held >= 6 ticks per attack yes without the held contact frame the hit slides instead of landing
distinct specials 12 4 defaults plus the shop alternatives
3D VFX calls in the kit 30 a special with no volume looks like a decal
FX strips / SFX files 24 / 28 no shared generic hit flash
element hue match 70% a fire hero whose effects are blue is not a fire hero

Today, with 20 heroes in the roster, 7 pass and 13 fail. That is the honest state and I publish it to myself every week. The script is the floor, never the ceiling: passing every number and still looking bad is a fail. The second half of the review is a contact sheet opened by eye — silhouette readable in pure black, secondary motion still moving after the body stops, the face changing expression between idle, hit and victory, trails thin and translucent, and a size check (anything more than 8% off the roster's 92 px median looks like a child standing next to the others).

Five rules that cost us real money to learn

An ability has to be an object you can draw, not an effect you can describe. One character burned three full rounds of rework. Her kit was "a shadow runs along the floor", "enemies miss for 3 seconds", "cold flame". Nothing to draw, so every attempt came back as a purple smudge, and polish cannot fix a concept problem. The characters everyone praised had nouns: a giant top hat, a sawing box, doves, a fan of cards, a spotlight, an ice lance, a comet, a ring of fire. The test I now apply before any production starts: describe the ability without the word "effect" and without a single colour adjective. If a noun survives that a draftsman could draw from memory, build it.

Big shapes are the worst place for solid colour. Domes, curtains, ground rings and auras are large by nature, and additive blending blows their core out to white. With 6 players on screen, one special like that erases the entire fight. Draw the readable shell, leave the middle nearly empty, and never hide the hero inside their own effect.

Launchers need an explicit launch height in pixels. Juggle decay was scaling the knockback differently depending on which tick the hit connected, so the same move launched to a different height every time and air combos only closed "sometimes".

Watch out for names the engine already owns. Eleven move ids have hardcoded behaviour in the fighter and the AI. A kit that reuses one inherits behaviour nobody asked for, and the symptom is silent — nothing errors, the move just acts strange. One character lost a round to exactly this.

Three rounds, minimum. Round 1 is a skeleton that runs. Round 2 is an animation pass done with the contact sheet open. Round 3 is VFX, SFX and juice. Everything we ever shipped in one round is in the bottom of the gate table today.

Yes, AI agents build a lot of this, and here is the bill

I run production through Claude Code with parallel agents. What makes it work is not the model, it is the paperwork around it: every task has a closed brief in docs/tasks/, and the repo has a file-ownership table so two agents never touch the same file. An agent gets the brief, the files it owns, the exact commands to validate with, and a response limit. The gate in the previous section exists precisely because agents — like people — report things as finished that are not.

I track spend per character. These are list-price API equivalents, not what I paid:

Character Cost (USD) Gate failures today
Roland (first hero) 2,509 5
Kael (second) 2,278 3
Lyra (third) 1,919 6
Bjorna (recent) 516 0 — passes
Risa (recent) 474 0 — passes
Maela (recent) 416 0 — passes
Orvyn (cheapest) 247 8

Project total so far: about US$20,000 in equivalent tokens.

The lesson is not "cheaper is better". The three most expensive characters are the three oldest: they paid for the exploration, and they fail today's gate because they were built before the style and before the rules existed. The cheapest character is also the worst in the roster. What actually correlates with quality is whether the written rules and the gate existed before production started. Once they did, a good hero landed in the US$400-900 band. Below roughly US$400 the quality visibly drops, and the rework costs more than the savings.

The stack, and ask me anything

Godot 4.7 (Forward+), GDScript for gameplay, Python with Pillow and numpy for every asset. The game is a 2.5D beat 'em up action-RPG with up to 6 players in local co-op: the simulation runs in pixels at 60 ticks per second, and the sprites are billboards lit inside a procedural 3D stage, HD-2D style.

Happy to go deeper on any part — the IK rig, the downsampling vote, the spring chains, the frozen animation contract, or how the agent briefs are written. Ask and I will post code.

reddit.com
u/Final_Huckleberry408 — 9 days ago
▲ 126 r/aigamedev

Claude 5.5 Opus is capable of building a full indie game. Truly, imagination is the limit. My project is 100% vibe-coded. Just made some fixes and had Claude update the game trailer!

u/Final_Huckleberry408 — 9 days ago
▲ 107 r/GodotEngine+1 crossposts

Grayfall 100% vibe coded ai game

A couple of days ago, I shared a post here showing some pixel art generated by opus 5.5. The response was awesome, and it inspired me to dive headfirst into actually making something with it!

About three days ago, I started developing a full HD 2D pixel art action game, and I wanted to show you all how it's turning out so far.

Here’s the trailer for the demo! I’d love to hear your thoughts, feedback, or any cool ideas you have. What do you think of the vibe and visuals so far? 🎮

u/Final_Huckleberry408 — 13 days ago