flutter3d_sim 0.5.0
flutter3d_sim: ^0.5.0 copied to clipboard
The simulation: a fixed step, an ECS, levels, navigation, saves and replays. Plain Dart, so it runs on a server.
0.5.0 #
Breaking. A step can say what happened, and four types stop being closed.
GameEventandGameEvents: what a step did, drained by whoever owns it. A buffer rather than a stream, because a stream delivers on a later microtask and two simulations here reproduce a recorded run exactly — an event arriving between two steps is the one state no replay can reproduce.ActorSystemwrites into the simulation's buffer at the moment of a death, so a monster killed by this step's shot lands after the shot that killed it; two lists read afterwards can only say that both occurred.ActorHurtis now the event rather than a value copied into one, andActorDiedsays who caused it.StepEvents.hasand.countanswer the two questions every reader asks of a drained step.Difficulty: four axes a genre applies where it decides. What the player is hurt by, what their attacks are worth, how quickly the opposition reacts, and how much of the genre's help is on. A value class rather than an enum, so a game writes its own — or builds one from a slider, which a list of four cannot express.opponentReactionis a duration, so the harder settings have less of it.ActivationOutcomeis open, andabstract base. Nothing ever switched over its three cases exhaustively, so sealing bought nothing and cost a game the ability to say what happened at its own door.StepSystemtakes aStepContext. A function type is frozen the day it is published; the context carriesdtand the phase, which the old shape could not, and can grow a field instead of breaking every system.Difficulty,Powers,ScoringandRunStats. Four pieces every genre wanted and at most one of them had written. Powers came out of the shooter, which keeps every question it answered and delegates the counting. Scoring is the question a tally cannot answer — what those counts were worth, and whether they came close enough together to be worth more. RunStats counts events, and counts nothing until a game says what to recognise.CharacterController.groundNormal— it measured the floor's normal to decide whether the body was standing on it and threw it away, so nothing above could tell a flat floor from a ramp.
0.4.2 #
- A brush can say how it casts, not only whether.
shadowCastingin the document is one ofon,off,doubleSidedorshadowsOnly— the engine has had four modes sinceShadowCastingModewas written and the format had two, so the two it could not ask for were the two it most needed: both faces recorded, for a wall one brush thick whose lit side and dark side are a metre apart, and a proxy that casts without being drawn.Brush.castsShadowis now the two-state view ofBrush.shadowCastingand goes on meaning what it meant, an unknown word is refused with the four in the message, and a document that said nothing goes on saying nothing. Surfaces are batched by the mode rather than by the boolean, because a batch is the smallest thing that can answer. - A breach keeps the baked light on the walls it did not touch.
Breaches.originssays which authored brush each current brush was cut out of, andBrushGeometry.build(origins:)uses it to find the planned face a piece's face is part of and measure its place inside it —LightmapLayout.uvOfPointandisOnPlaneare that arithmetic. Before it, redrawing a level after one hole meant no atlas at all and every wall in every room fell back to flat ambient at once. The faces the blast itself made take the neutral texel, which is the one part of a breached wall nothing ever baked. - A level can ask to be reflected.
EntityTypes.reflectionProbeis the format's word for a point a room is reflected from, andReflectionProbeKindis the kind that validates one:radius,intensity,faceSize,levels,nearandfarare all optional and each is refused where the renderer would otherwise assert on it at load. The two planes are resolved against the probe's own defaults before either is judged, so a document naming only a near plane past two hundred metres is refused here rather than at load: the far plane it did not name is still a far plane. Pure data to the simulation — nothing spawns and nothing is revealed — and a word a game has to put in its own vocabulary, so a game without probes reads the entity as unknown rather than growing a reflection it did not ask for.
0.4.1 #
- Lightmaps.
LightmapLayoutunwraps every visible brush face onto a planar atlas from the level alone, so the baker and the geometry agree without a table;LightmapBakerbakes the light the walls throw on each other by gathering — direct light with shadows through the level's own collision world, then bounces along cosine-weighted rays — seeded by the texel, so two bakes are the same bytes.Lightmapstores RGBM in RGBA8 with a hash of the brushes, lights and materials, anddart run flutter3d_sim:bake_lightmapwrites<level>.lightmap.bin.BrushGeometry.build(lightmap:)hands every vertex its second coordinate. - Jump links.
NavGrid.bake(jumps:)finds the gaps and ledges aJumpReachclears; aFlowFieldfilters them by its own body's reach with the body's width added, relaxes them backwards at their distance plus two cells so a walk of equal length wins, andjumpAtsays when the next step is a jump.Navigation.jumpAheadandActorSystemtake off through the controller's buffered request on the take-off cell. Mind.heading, for turning to face the way the field said.
0.4.0 #
- First release. It is
flutter3d_game's inside, moved out whole: the fixed step, the entity store, the level format and its validator, saves, demos and the rewind buffer, world logic, actors, navigation, the camera rig and the maths. Nothing changed behaviour; the imports moved and the package boundary is new. - Plain Dart, and that is the reason it exists. A server that verifies a
submitted run has to replay it through the same simulation the player ran,
and a Flutter SDK in that container is a blocker rather than an
inconvenience.
flutter3d_gamekeeps the eight files that reached Flutter — the touch and keyboard widgets, theMediaQueryread, the diagnostics sink — and re-exports this package, so no existing program changes a line. - The boundary is a check, not a comment:
the simulation names no Flutterintool/structure.dartscanslib/,test/andbin/. StateDigestandDigestTracecome with it — a 32-bit digest over the bits of a snapshot, computed the same way in a browser as in the VM, and a checkpoint trace that names the first step two runs disagree at.dart run flutter3d_sim:bake_visibilitymoves here fromflutter3d_game, where it had never needed Flutter either.