2025.3.1 released! Highlights + notes

2025.3.1 is available for testing!

Use version control/backup (always) and switch your modules.lx version over to try it and let us know if you run into any issues.

Scenes

Scene data

Scenes now have a Data definition, similar to how modifiers do!

This allows storing data on the scene itself, without having to create a whole modifier for that. While you still can of course, this is just streamlined. When selecting a scene in the editor, you’ll now see the Scene data to edit those values, and is a good option for passing entities from the scene into the scene script via a Link field.

Along with the data comes the new generated script.api helper file (you’ll see this on disk temporarily, see below). This enables the states and will be used for more convenience later.

Minor breaking changes

Note: If you had a scene.wren scene script from before, there are some minor changes required to update. You can do this manually, or you can generate a blank/new file, and copy your code into it. It’s mostly the preamble parts that have minor changes.

If you have issues, please ask! There’s some workarounds depending on the shape of your content but it should just work!

  • add import line for the newly generated api helpers
  • remove the import "luxe: world/world" for Wire line
  • change the Entry is SceneReady => Entry is Ready
  • add #scene tag to the Entry class
  • add a data class above your entry class
import "scene/example.scene/scene.api" for Ready, Wire, Fields, State, Op

...

#block = data
class Data {
  //put your scene data here
}

...

#scene
class Entry is Ready {

States

State machines in games are really common and very useful for gameplay code, so this build introduces the initial convenience of states directly into modifiers and scene scripts :sparkles:

The style of state machine is a declarative approach using a builder/fluid style API, and supports transitions from modifier/scene tools like the per entity data, wires, time and so on.

A simple example would be a menu.scene you create, where it might have main, settings, credits states for the menu. This is what that looks like right now, in the scene script:

That’s it! The names match the variable, this is important for later as we’ll see.

Our scene or system also has the states variable, which we use to change states directly like so states.goto(scene, main). We do that inside of ready() in a scene script to set the default state. We can also add behaviour to the state, like so:

Here we have a function that’s called on entering the state, leaving the state, and what we call a transition. This transition is a function that returns true or false, and if it does, changes states to the given state (in this case, pressing escape will go back to the main state).

Nested states

A common pattern that can reduce code complexity and allow sharing more code across states, is nested states. This has different names like nested states, parent states, HFSM (hierarchical finite state machine) and such.

These states typically run at the same time as their child states, allowing the transition code to happen at the parent and be defined once only, and handle specifics in the sub state.

As an example, take an a character that has an idle state. Within our idle state, we might decide to tap our foot, look around nervously, check our watch, wave at someone, and yawn. Each of those can be viewed as a sub state of the idle state. To the ‘outside world’ we present a simplified interface, we’re in the idle state, but within that we’re doing all sorts of stuff.

Let’s see how we declare that now, and this time we’ll do it inside a modifier (per entity we’re attached to).

This API is declarative, this code doesn’t do anything per se, it just defines the state hierarchy for us! This also shows why we use the names, notice how the type of the idle variable is a known type.

That means we get nice completion on our states, like this:

Simpler states
For the simpler states like looking around or tapping a foot we can do something simple, like this:

Notice that we do it inside attach, we’re definition the idle behavior for that entity.

Looping states and transitions
For something more elaborate, we often want a transition state as well. If a character is sprinting, and then comes to a stop, it would make sense to do sprint -> slide + stop -> idle - here we have a ‘pre’ state for idle.

Another example of more detailed behavior is a looping state, where we have a begin, loop, and end section of the state. Checking a watch is an example here: pull the watch out of your pocket (begin), stare at the watch (loop), put the watch away (end).

Like before, from the outside it appears as a simple “check watch” state, but contains more nuance.

The example below is spelled out, but shows how you could do a random amount of time checking the watch, between 1 and 4 seconds of a loop in the middle of the begin/end. You can also add transitions on the watch state (above the nested ones), make the looping state wait indefinitely (show this state while player is holding down the button, until we eat something, etc).

Transition helpers

We’ve seen goto_after but there are a handful of other helpers in the first build with states.
Wires are the event system backbone for scenes and modifiers, so if we receive an event we can use that as a transition. We can also key on our Data class, listening for changes and responding.

Other notes

We have more transition helpers coming, as well as making it easy to add custom game specific transitions allowing expressive easy to understand declarative gameplay code in the same workflow.

You can mark a state as persistent, root.persists(true) will keep the root state active and running alongside other states. This is useful for ‘watcher’ states that handle transitions at the higher level.

You can do stuff on reenter, if for example you go from idle.yawnidle.stand, you’re staying in the idle state, so it’s not “entering” the idle state, but it does notify that it is reentering.

Next time (mini roadmap)

Showcase
We’ve been working on a showcase demo that we’ll post about when it’s ready.

Script compiler
There’s an updated script compiler and moving the scripts to the new asset workflows. This brings a bunch of benefits, speed ups and more.

The generated helper files (like *.modifier.api.wren and scene.api.wren files) are temporarily visible in your project, these will soon be moved into the .luxe/ data folder where they were meant to live.

Anim
And of course we’re wrapping up the new animation workflows!

Fixes and tweaks

  • BREAKING; minor; Scene; scene class tweaks and changes for data + states
  • Scene; add data, states, and change events
  • Wren; fix modules being pulled out from functions by GC when unloaded
  • Camera; set2D now updates the block as intended (wip)
  • Blocks now support referenced + untyped types, unlocking a ton of things
  • Assets; init phase for early and correct timing handling of block defs
  • UI; block view crash fixes on empty blocks
  • Wren; fix a script compiler crashes and bugs
  • Sprite; add get/set pixelated api that was missing
  • Outlines; tidy up the warnings and stuff in outlines
  • Render; fix some outdated defaults for LoadAction
  • World; add no_cull option for world render desc
  • Math; add missing divide2D
  • meshoptimizer 0.23
  • Jolt 5.3.0

:warning: [LINUX USERS] :police_car_light:

This release was uploaded with an outdated shader compiler. Builds will unfortunately fail on this version in Linux. The next release will fix this issue. Recommended to stay on the previous release until then.

If you need a fix sooner, contact a luxe engine developer.