Skip to content
kabartayPublic

About

An open world that comes alive as you build in it — a 3D exploration-and-building game made for my children, on Godot 4

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Aava

CI Release DOI License Art Godot Platform Privacy

An open world that comes alive as you build in it.

Aava /ˈɑːʋɑ/ — Finnish for vast, open expanse, said most often of the open sea (aava meri) or a wide, unobstructed landscape. Where the word itself comes from is not settled: it may be a variant of aapa, the term for the open, treeless mires of Finland's north, shifted by association with avata, "to open"; or a separate borrowing from Sámi. Either root points the same way — a country you can see all the way across, and build anything in, which is the freedom the game is actually built around. Finland is also, out of all proportion to its size, a games country — Supercell, Rovio, Remedy — and a small project made by one person on evenings and weekends is not a strange place to borrow a word from it.

Aava is a 3D exploration-and-building game for children, written in GDScript on Godot 4. The valley is a pure function of one integer seed: the ground, the river, the forest and the mountains are computed rather than stored, so nothing about the world has to be saved, sent between two machines playing together, or loaded from a file. What is saved is only what a child has changed — see Every world is one integer, below.

The loop is creation rather than collection. Everything built makes the valley more alive, and the more alive it is the more it gives back: plant a grove and birds arrive, feed an animal and it becomes a friend, raise a house with a bed and a fire and it becomes somewhere to actually spend the night rather than a shape standing in a field. Coins come from looking after things, and are spent on tools that widen what a child can reach — an axe, shears, a lantern, a bicycle, a motorcycle, a quad, a horse to ford the river a wheeled machine cannot — rather than on numbers going up.

Since the first line of this file was written, the valley has grown mountains with snow on their shoulders and ice in their hollows, a river long enough to be worth following, two lakes — one of them up a hillside, holding its own water at its own height — a day/night cycle with a lantern for the dark, a bow range, a herd of twenty horses and a shop to buy a mount from, a flock of sheep to shear and cows to milk, an economy where felling a tree costs what growing one paid, campfires that need feeding and beds worth sleeping in, animals that speak on their own terms, and a whole ambient soundscape synthesised from nothing but code.

There is a fairground across the river with a carousel, two moving walkways, a trampoline, a fifty-metre wheel, a drop tower, a pirate ship and a wooden coaster with two vertical loops, whose bars come down before anything moves, and whose clocks the host's phone shares with a guest's, whose speed is integrated from the slope of its own track — the loops are sized by what the drop actually pays out, v² ≥ 5gR, rather than by eye. A bridge crosses to it, because a machine on wheels cannot ford the river and a horse can; street signs stand where the four roads meet, each turned by asking where its own road actually goes. And a way to invite another player into your own copy of the valley over the local network, and talk to them while you play.

Running it

Requires Godot 4.7 or newer. No other dependencies.

godot --path .                       # play
godot --path . -- --seed=12345       # play a different valley

Move with the on-screen stick or WASD, look around by dragging anywhere else on the screen, sprint with shift, jump with space. Walk over a stick, a stone, a reed, a seed or a cone and you pick it up — there is no button for gathering, because walking into things is something a six-year-old understands without being told. Tap build to open the palette; the ghost shows what will be placed, green when it can be and red with the reason when it cannot. See docs/CONTROLS.md for the full list.

Walk south-west and you find a football pitch by the river. Stand next to a ball and a kick button appears; press it, or E, and the ball goes. Hold it and slide the thumb to turn or lift the shot; run at the ball and it goes much harder — that is the whole skill of shooting, and it is worth more to a six-year-old than any charge-up meter. Balls that end up in the river come back on their own.

Plant three saplings close together, keep playing, and they grow. When all three are full trees the place becomes a grove and birds arrive. That loop — build, and the world answers — is what the whole game is for. The stick is Godot 4.7's native VirtualJoystick, so both fingers work at once without any index bookkeeping: the viewport keeps routing each finger to the control it first landed on.

Walk north-east and there is a shop: a green-boarded barn with a porch, with five bicycles and two motorcycles standing on its floor and somebody behind the counter. Its shelf is pictures and prices, because a shop of words excludes the child most likely to be saving up; tap a picture and it says what the thing is, tap buy and it is yours. The coins come from looking after animals, and from the wool you cut off the sheep — the one thing the shop buys rather than sells.

Build a house, feed its campfire, and sleep in its bed after dark to wake up rested at dawn — the reason to have built the house at all rather than a shape standing in a field. Invite a sibling into your own copy of the valley from the ≡ menu, and talk to them by holding talk: push-to-talk only, the microphone physically stopped between presses, and reachable only by whoever you actually invited.

Every world is one integer. There is no hand-placed scene content anywhere in this project: the valley, the mountains, the river, the lakes and everything scattered on them are generated from the seed, which is the only way one person can make a world large enough not to be exhausted in an afternoon. It also means any bug can be reproduced by sharing a number, and that two devices joined over the family network never have to send each other the ground — both already made the identical valley from the same seed.

Looking at it without the editor

This project is written from a terminal, so there is a tool whose whole job is to answer "what does it actually look like right now":

godot --path . res://dev/capture.tscn -- \
      --out=/tmp/shot.png --pos=-2,4,30 --look=26,1.2,-6 --time=0.40
Flag Meaning
--out=PATH where to write the PNG
--pos=x,y,z camera position
--look=x,y,z what it points at
--time=0..1 time of day, 0.5 is noon
--seed=N which valley
--nowater=1 hide the water sheet, to see the ground under it
--unshaded=1 draw terrain as flat vertex colour, no lighting
--player=1 spawn the real player and shoot through the game camera
--yaw=DEG, --pitch=DEG aim that camera
--demo=1 stand a finished camp and a grown grove in front of the player
--build=1, --house=1 open build mode; add a finished house nearby
--atpitch=1 stand the player on the football pitch
--kick=SEC kick the nearest ball, then watch it fly for this long
--mounts=1, --animals=1 put a horse and a bicycle, or one of every animal, in view
--dam=1 finish the first beaver dam, to show the pond behind it
--shop=1, --together=choice|host|type open the shop, or the play-together panel, on a chosen page
--lang=en|fr|ru which language to draw the interface in

That is a representative subset — src/dev/capture.gd's own header comment is the exhaustive, current list. The last two rows above exist because a screenshot cannot tell you whether the colours are wrong or the light is wrong, and guessing between the two is expensive. The tool also prints how many chunks and triangles were actually built and where the camera ended up — which is how the inverted triangle winding that made the whole valley invisible from above was finally caught.

The checks CI runs, which are worth running by hand before a commit:

godot --headless --fixed-fps 60 --path . --script src/dev/ci_check.gd

Every assertion in there corresponds to a bug that actually happened. The one that earns its keep most often is the pickup count: it caught a world with nothing to find, because resources were being built in _ready, which never runs for a node used before the scene tree starts. See docs/LESSONS.md for the whole catalogue — it is long, and every entry in it is a bug that cost real time and is now guarded against.

On an Android tablet

The Android preset is committed, so a build is one command. It uses Godot's prebuilt template rather than a Gradle build, which means no Gradle and no Android project directory — just the SDK and a JDK.

godot --headless --path . --export-debug "Android" build/aava.apk
adb install -r build/aava.apk

What has to be set once, in Godot's editor settings:

Setting Value
export/android/android_sdk_path ~/Library/Android/sdk
export/android/java_sdk_path a real JDK 17+
export/android/debug_keystore a debug keystore
export/android/debug_keystore_user androiddebugkey
export/android/debug_keystore_pass android

Two things that cost time if you do not know them. /usr/bin/java on macOS is a stub that only tells you to install Java, so java -version succeeding proves nothing — point java_sdk_path at a real JDK, such as the one Android Studio bundles at Contents/jbr/Contents/Home. And rendering/textures/vram_compression/import_etc2_astc must be on or the export aborts, because Android requires ETC2/ASTC textures.

This is the actual target device, not an afterthought: the game has been installed and played on a real tablet repeatedly, which is how bugs invisible in a desktop screenshot — the game locked to portrait, a player launched into the sky while swimming, a horse vanishing the instant it was mounted — were actually found. See docs/ANDROID.md for the toolchain and what the performance numbers actually say.

iPad needs a paid Apple developer account and is not set up yet.

Two traps worth knowing

Anchor ignore patterns. A plain build/ line in .gitignore matches at every level, including src/build/. The whole building system was silently kept out of the commit that added it: the game ran perfectly here and CI failed with Could not find type "Structures", because CI was the first machine to see the tree as it had actually been pushed. Ignore patterns for build output are now written /build/.

Import twice on a clean checkout. Global class_name types only exist once .godot/global_script_class_cache.cfg has been written, and on a fresh clone that file is written by the same pass that parses the scripts. Your own machine never shows this, because the cache is left over from last time.

godot --headless --path . --import   # writes the class cache
godot --headless --path . --import   # this one should be silent

To see the project as a fresh clone sees it, delete .godot/ first — and check git ls-files src against what is actually on disk.

Layout

src/
  main.gd              entry point: reads the seed, builds the world and the game
  world/
    height_field.gd    the shape of the world as a pure function of (x, z)
    terrain_spec.gd    shared terrain constants, dependency-free on purpose
    terrain.gd         chunk streaming and detail rings
    terrain_chunk.gd   one square of ground: mesh, vertex colours, collision
    water.gd           the water sheet and its shader
    atmosphere.gd      sky, sun, a full day/night cycle
    plant_meshes.gd    trees and grass, built from primitives in code
    vegetation.gd      planting, streamed in tiles around the player
    vegetation_tile.gd one tile of planting: a MultiMesh per plant kind
    world.gd           assembles the above; the one place a world comes from
    pickups.gd         sticks, stones, reeds, seeds and cones, gathered by walking,
                       and laid again out of sight after a while
    birds.gd           the world answering: birds over feeders and groves
    pitch.gd           where the football pitch is, and how it levels the ground
    lakes.gd           two lakes, each holding its own water at its own height
    distant_land.gd    the far country: one coarse ring out to the mountains
    paths.gd           worn routes between the places worth going
    place_spec.gd      where the playground, pool, café, shop and butts stand
    places.gd          the buildings and what they offer: the swing, the slide,
                       the café's meal, the pool's turnstile, the shop's counter
    dam_spec.gd, dams.gd     a beaver dam site, and what it does to the river
    animal_kinds.gd, animals.gd   six species: habitat, shyness, and what each
                       one gives — coins, or wool from a sheep and milk from a cow
    animal_collision.gd, tree_collision.gd   what is solid near you, in a small
                       pool of bodies that follows the player about
    ducks.gd           ducks on the ponds, which give nothing and are the point
    mount_kinds.gd, mounts.gd     the horse, the bicycle, the motorcycle, the boats
    hearths.gd         campfires: fuel, warmth, the flame itself
    park.gd, park_spec.gd   the fairground and where it stands; its rides:
                       carousel.gd, ferris_wheel.gd, roller_coaster.gd,
                       drop_tower.gd, pirate_ship.gd, walkway.gd
    fair_decor.gd, ride_sounds.gd   its lamps, stalls and signs; its hum
    bridge.gd, bridge_spec.gd, signpost.gd, plaque.gd   the crossing and the street signs
    clear_ground.gd    ground that is built over, where grass must not grow
    respawn_spot.gd    where a child is saved and may reappear
    animal_loft.gd, animal_natural.gd, animal_small.gd, horse_natural.gd
                       the animals as smooth painted skins
    wind.gd, flag.gd, sail.gd   one wind, and what flies in it
    boulders.gd        the jumpable rocks
    felled.gd          stumps left behind by the axe
  football/
    ball.gd            a football, with physics rather than an animation
    goal.gd            posts you can hit and netting that kills the ball
    football_ground.gd goals, balls, scoring, and fetching strays
    kick_preview.gd    where the ball will go, drawn while you wind up
    pitch_markings.gd, scoreboard.gd   the lines and corner flags, and the board
  archery/
    archery.gd         the range, the butts, and the bow itself
  player/
    player.gd          the controller: acceleration, coyote time, jump buffer
    camera_rig.gd      third-person camera that glides instead of ticking
    character.gd, rig_motion.gd, seat_pose.gd   five characters, how they move,
                       and how they sit in every seat
    lantern.gd         the light a child carries after dark
  build/
    build_kinds.gd     what can be built, what it costs, what it grows into
    house_parts.gd     walls, doors, windows, floors, roofs, stairs, a bed
    structures.gd      what has been built, and what it has since become
    plans.gd, venues.gd, keep_out.gd   ready-made buildings, what a built café
                       or shop does, and ground that belongs to something else
    build_mode.gd      the ghost preview, snapping, and the one overlap rule
  shop/
    wallet.gd          coins, and what has been bought
    shop_stock.gd      what is for sale, and what it costs
  game/
    item_kinds.gd      what can be picked up
    inventory.gd       what is being carried
    vitals.gd          energy and water
    journal.gd         what a child has done, counted rather than owned
    today.gd, tasks.gd one thing worth doing each day, and the opening steps
    profiles.gd        players, map templates, and the worlds made from them
    save_game.gd       the save file, as readable JSON, split by player and world
    dishes.gd          the café's six dishes
    wiring.gd          how the interface and the game are connected, once
  net/
    session.gd         LAN multiplayer: hosting, joining, what gets sent
    visitors.gd        drawing the other children in the valley
    ride_sync.gd       the host's rides, followed by the guest's phone
    voice.gd           push-to-talk voice chat
  audio/
    sounds.gd          short sound effects, synthesised at load
    ambience.gd        wind, water, birds and leaf rustle, positional and looping
    animal_voices.gd   barks, chatter, grunts and purrs, as single events
    mix.gd             the buses: a limiter on Master, World, Effects
  ui/
    camera_pad.gd      the look-around touch layer
    hud.gd             every on-screen control
    minimap.gd, map_arrow.gd, place_glyph.gd   the map, the arrow that shows
                       which way you face, and the picture for each destination
    map_view.gd, map_gesture.gd, map_painter.gd   where it looks, how fingers
                       move it, and how it is painted
    map_guide.gd, guide_badge.gd, map_route.gd, map_legend.gd, map_tool.gd,
    map_compass.gd     the chosen place and its pointer, the route, the key,
                       the round buttons and the peak that says north
    hold_ring.gd, volume_look.gd   a press that must be held; the volume sliders' look
    hud_fly.gd, note_icon.gd   a coin or a chip flying to the purse or the bag;
                       the picture beside an announcement, for a child who cannot read
    look_picker.gd, character_icon.gd, flag_icon.gd, dish_icon.gd, coin_icon.gd
                       choosing who you are and the other drawn pictures
    action_icon.gd, item_chip.gd   every button's picture, and every item's,
                       drawn in code rather than loaded as art
    backpack.gd, vitals_gauge.gd, part_icon.gd, shop_icon.gd   the rest of the interface
    together_panel.gd  hosting, joining, and the join-code keypad
  i18n/
    text.gd            every user-visible string, in English, French and Russian
  input_actions.gd     input actions, registered in code
  dev/
    capture.gd         screenshot tool
    toddler_fuzz.gd, net_pair.gd   a random-tapping child; two processes as a pair
    net_day.gd, net_rogue.gd   a scripted twelve-minute day of two phones, and a
                       misbehaving peer that the limits must hold off
    ci_check.gd        the numeric check suite CI runs on every push
    perf_log.gd        which step of the frame was slowest, named in the log

height_field.gd is the single source of truth for where the ground is. The mesh, the collision heightmap, and every tree, animal and building ask it — there is deliberately no second opinion about the ground anywhere, which is what keeps trees from floating and the player from falling through hills. See docs/ARCHITECTURE.md for the rest of how this is put together, and docs/STATUS.md for what is actually finished today.

Design

See docs/DESIGN.md.

Citing

A CITATION.cff sits in the repository root, so GitHub's "Cite this repository" button renders BibTeX and APA directly. The same entry, to copy:

@software{Organokov_Aava_2026,
  author    = {Organokov, Mukharbek},
  title     = {{Aava: an open world that comes alive as you build in it}},
  year      = {2026},
  version   = {0.8.0},
  doi       = {10.5281/zenodo.22965441},
  url       = {https://github.com/kabartay/aava},
  publisher = {Zenodo},
}

There is no paper. This is a game made for two brothers, and what there is to cite is the thing itself — the software, and whichever build of it you played.

Every release is archived on Zenodo with a DOI of its own. The concept DOI 10.5281/zenodo.22965441 always resolves to the newest release; a version DOI — v0.4.1, for instance — stays on the build it was minted for. Cite a version when it matters which one somebody actually played, because a valley three releases old is a different valley: it had no fairground, no bridge, and horses that faced the wrong way while grazing.

Citing the software is not permission to use the name or the icon — those are reserved, and the Licence section below says what that means.

Licence

Code: AGPL-3.0. See LICENSE. Read it, change it, build it, play it; a changed version you pass on has to stay open too.

The name and the look are reserved. "Aava", the icon and the visual identity that goes with them are not covered by that licence — see NOTICE.md. Fork it under your own name and your own icon and you are welcome.

About

An open world that comes alive as you build in it — a 3D exploration-and-building game made for my children, on Godot 4

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages