luxe 2024.9.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.
Introducing Wires (not public yet!)
Please let us make a proper blog before you show this to a lot of people or in public discords!
Make a trigger. Make a door. Now… go to code and make them work. What if you could connect them directly in editor?
Switch → Light. Button → new_game()
- wires are a connection between entities (via modifiers + scene scripts).
Wires are a tool we had been wanting to add for A Long Time, and we’re excited to be at the point where this is available to test.
What are wires? If you’ve used Qt or any other signal/slot UI system or event based thing before, this should be familiar.
We also think making games should be a little fun, so here’s how they look in motion:
A wire is the ability to send a message (with data) to anyone interested in listening.
There’s two sides: A sender and a receiver. They don’t know about each other, but they can be connected.
Inside a custom modifier, or a scene script, create a function like this:
#wire = 1
my_function(entity: Entity) {
Log.print("received a call for entity %(entity)!")
}
That’s it! Wire ids are simple numbers and should be unique within your modifier/scene script.
Now your modifier can be sent a message, from any other wire (of the same type).
How about sending a message? That’s easy too. Declare a wire variable, and call
#wire = 2
var release: Wire = Wire.create()
//...
release.send(entity)
Types… data…
See below, but a wire can send a block along it.
import "data/toggle.block" for ToggleData
//...
#wire = 2
#type = "data/toggle.block"
var release: Wire = Wire.create()
Later, when you want to send it:
var toggle: ToggleData = release.prepare()
toggle.state = true
toggle.ratio = 0.5
release.send(entity, toggle)
On the other side:
#wire = 1
my_function(entity: Entity, toggle: ToggleData) {
Log.print("received a call for entity %(entity) with state %(toggle.state)!")
}
Work in progress
As always, this is new, there’s rough corners but let us know!
To use it in the editor, attach a Wires
modifier to an entity (usually the sender), then select it and hit Wires
on the right. Then drop entities onto the Send
or Receive
side, to start configuring them. Any wires visible will be shown there.
Here are some other highlights!
Modifier workflow automation
Make a blank file called system/some.modifier.wren
and save it, hit build, and the engine will populate the modifier with a template.
Edit that and you’re on your way! We’ll expose this in the editor soon.
Modifier change handler
With the #emit_changes
on your system class, your system will notify you when a field on an entity has been modified.
This is especially useful for editor related things but of course, is needed often.
Add the import for your fields enum:
import "system/puppet/interest.modifier.api" for API, Modifier, APIGet, APISet, Fields
Add the tag to the system:
#system
#phase(on, tick)
#emit_changes
class System is Modifier {
Listen for changes
change(entity: Entity, change: ModifierChange) {
var data = get(entity)
if(change.field_id == Fields.my_field) {
//respond to change
}
}
Modifier buttons
Leveraging the change system, if you make a number field that has a #button
tag,
the editor will turn it into a button. See here in the discord
#button = "add interest nearby"
var add_interest: Num = 1
Clicking the button is number += 1
, so you can respond to the change via the change listener.
Modifier Object/Array values (minor breakage)
Previously when accessing a field of a modifier or asset that was a block, it would return the raw block and you’d make a wren object from it.
This isn’t the case anymore, it will act as if it was a regular wren object, allowing accessing the fields directly.
This is true for arrays, and arrays of objects. Previously adding a value to a block array was manual, now you can do:
var list = data.items
list.add(value) //if primitive only!
var obj = list.add() //objects
obj.field = 3
Keep in mind, these values are a little special, so cache a local variable and reuse it.
Also try not to keep references, as they can point to dead entities or instances
Stage type
Often when working on multiple scenes, you want to open them all as one unit.
This is called a Stage, it’s a container of scenes to load.
You can do that now by creating a stage type using the editor.
These are not treated any special way, it does what you think it does, and is wip.
e.g there’s no way to unload the grouped stage, and no way to edit the asset in the UI.
You can edit the file of course, it’s a simple lx file.
Skeleton blend graphs and animation
Something we’re using in archives and other 3D users will find useful!
We’ve been adding these in the background for a while, but we now have easy to use blend graphs. This allows complex character behaviours expressed in a simple way.
Here is how you would have a walk animation, followed by a hand IK node, and a head look at IK node.
There’s Blend
, Two Bone IK
, Look At IK
and CCDIK
to start, with more to come.
Here’s how that looks:
Block types
Often you might want a block definition for your own needs (as we’ll see later).
You can now create a my_type.block.wren
- with the .block.wren
extension.
Here’s a toggle.block.wren
type we can use later, for example.
Note these complement asset types, you don’t need to use these for custom asset types!.
#block = data
class ToggleData {
var state: Bool = false
var ratio: Num = 0
}
Other notes
Agent
The agent is sometimes unreliable, sometimes memory hungry and sometimes created many instances. We’ve tracked down at least a few potential causes and are continuing to make it rock solid. Let us know your experience!
Fix error about omni.wren
Was a minor nuisance but should be gone now.
Draw style helpers
Often it’s nice to reuse a path style, now you can modify some properties inline:
Draw.line(.... , style.color(Color.pink).thickness(2))
Draw.line(.... , style.color(Color.black).thickness(0.2))
Entity.name(entity)
No more %(Strings.get(Entity.get_name(entity)))
!
Just Entity.name(entity)
will suffice. It also handles invalid entities! Much nicer.
Transform look at inverted
When using Transform.look_at
the vector was flipped (because of camera usage being a common use case).
A little oversight now fixed. If you find your look at is wrong, there is now an invert
argument to use it both ways.
This is cos camera forward
vector isn’t in the same direction as the look direction cos reasons.
Var explicit initialization not happening sometimes
We found a gap in the var
declarations in classes when inheriting!
If you were using class vars with custom modifiers or scene scripts, and they weren’t initializing, add this:
//required atm
construct new(world: World) { super(world) }
The parent class initializes itself, but the child class isn’t given the chance.
We’ll fix that but for now you can work around it.
Other Highlights
- IO; add
Handles
, this allows storing Wren objects in a numeric Handle (like luxe does), for e.g storing in a modifier field - String;
Str.template
for templating strings - Script; speed up wren compiling quite a bit
- Cable; a simple 2D/3D cable simulator is
luxe: cable
- Sprite; access to the effects API from code is now on Sprite API
- Modifiers; better error messages in cases where it’s unclear what’s going wrong
- Modifiers; access to
Thing.id
e.gMyModifier.id
for the type id (built in wip) - Modifiers; fix
#phase
tag, can now used as intended e.g#phase(before, tick)
and#phase(after, late)
- Block; options type would get confused about which type it was when no default was specified (arrays, usually)
- Block; ui fields for link types allow dropping onto the array itself, much faster workflow
- UI; Tabs; change events for tab changing
- Assets; don’t recompile fonts + meshes every time, use the cache as intended
- Physics; physics assets visible in new world editor
There’s a lot
Of things in there and we’ve got more landing soon, so let us know how it goes for you.
Enjoy!