Oh it's 2020!
What happened during 2019, and what's intended for 2020? Let's find out. 2019 was a pretty busy year on the personal side (had a child, changed jobs etc) but there was still a reasonable amount of forward movement and introspection on luxe.
Reminder that luxe is not available for download yet, this version of the engine you're reading about is in closed testing at the moment.
luxe 101
As I start to talk more about how the engine works, it would probably make sense to explain what it looks like from a user perspective, so that the words I'm using make sense! So let's have a brief look at the terminology and structure that make up a luxe game.
This is taken directly from the introduction of the new luxe user guide.
World
In luxe, things in your game are represented inside of a world.
A player character, level elements, UI elements, all of these typically exist in a world.
The world gives you the tools to manage your things, and lets you create spaces where your game can happen. You also often have more than one world, like a UI world and a game world.
Entity
In luxe we call a thing an entity.
Entities are the things that exist in a World
, like the player or even a menu item.
An Entity
is how we talk about a unique thing, which makes it the basic building block of making games in luxe!
Modifiers
An entity just exists, it can't do anything yet.
To make an entity do something, we attach modifiers to it!
A modifier expresses something you want the entity to be able to do.
If you want your entity to look like a triangle, you attach a modifier to express that. If you want it to bounce around with some physics too, another modifier is added.
Modifiers can be added or removed on demand, allowing an entity to change in response to gameplay, which is surprisingly flexible. What an entity looks like, how it behaves, and what it actually is can change during the game, just by attaching and detaching modifiers.
Systems
Systems are what power the modifiers.
A modifier is added to an entity through a world system.
When you talk about a modifier, you talk to the system that provides it. A modifier describes what you want, but the system is what actually makes it happen.
For example, attaching a Transform
gives an entity a position in a world. You ask the Transform
system to attach it, and then you can ask the system to move it around.
Services
Some systems exist at a lower level, powering everything.
We call these low level systems a service API.
These are systems that serve a general need beneath the other systems. For example, rendering (drawing things) is a system that acts as a service. It is used by higher level world systems to provide your game with drawing.
Modules
luxe is designed as a rich set of systems - ready to make games - but it couldn't possibly provide all systems that all different types of games may need without becoming too big and unfocused.
With luxe, modules solve this by providing a way for you to expand the tools, systems and services that are available to you as a user.
luxe is designed for modules to serve this need, which makes them a fundamental part of using luxe. In fact, the luxe API itself is a module too!
That's it for the guide intro bit! Let's talk about how that fits into games, and how games fit into this.
Game specifics
As luxe is designed around empowering game specific workflows, there are two underlying concepts here. One is foundation which is stuff that is immutable/unchanging, the way it works.
The other is flexible, and provides space for your game to do whatever it wants/needs. This model was discussed all the way back in dev log #3, where I was talking about things being too flexible, which is actually unhelpful to our goals.
The approach of "everything is optional" taken too far breaks the design goal intended by the core being a foundation to build upon. It's difficult to build above a foundation that has a lot of holes in it...
...
The core must contain all the foundations to build on, and they must be unified to work together as as whole.
Foundations
So in context, the entity, modifier and world concepts are deep foundations. These are the building blocks that make the foundation concept actually work.
To bypass those, would basically be writing a different engine. You could, nothing would prevent you from doing it, but you'd have to leave behind roughly 99% of luxe itself, and most of the modules, because those use the foundation... by design. It's what makes this whole thing a thing!
And to be clear, the foundations are still flexible. For idiomatic, and easier game specific code, you'd be relying on them since that's how it works.
Services
An important concept in this flexibility is the service APIs, where you're writing code that is independent and helps game specifics/flexibility. Quite often though, you'll want them to exist at the world level too.
An obvious example is physics, like bullet physics, which is a service API. However, you almost certainly want to attach a body and shape modifiers to your entities, working on the high level, rather than manually fuss around with the low level API every time. Isn't that the point of using a game engine!
The key factor here is that the service APIs are (ideally) accessible below the high level, they don't block you from using them, for the cases where you do need to get more specific. This is a key pillar in the luxe design, things are bottom up.
Another example is how the Text
modifier, a world system, offers a way to render text attached to an entity. This is backed by the Render
service API, so you can do your own thing by using that API directly instead. The text itself is backed by the Geometry
service API, so again if you need to, just use that.
Of course, by dropping to a lower level, there are times you lose the higher level conveniences and have to account for that with more code. But this is kind of the point too, you're doing something game specific, it needs specific code.
2019
System revisions
At the end of the previous dev log, #8, there was a section about what's next for 2019:
There are a few systems that need to be completed before preview, namely the animation system is undergoing a bit of a refactor after some higher level changes, and the particle system has been implemented but lacks any user facing APIs yet, and the prototype system (prefabs) had to be refactored based on findings in practice.
The animation system and prototype system got (almost) all their necessary redesign and rewrites, so that's good! The particles remain in limbo currently, as they aren't as blocking as core workflow issues (their time is coming soon).
There's a few key fixes that have to come in before those can be called done, but they're pretty much usable as intended.
luxe 2019 in summary
A lot of time spent on luxe this year has been spent on refinement, taking the iterative first passes and moving them a more user ready version. A lot of refactoring and renaming of APIs, finalizing design details, and validating them by making stuff.
The engine also underwent a lot of refactoring on the rendering and materials side. The goal here was to fix up inconsistencies, get around to implementing features that were designed a certain way (but didn't work that way yet) and to unify the way material inputs work. This is demonstrated later in the render module section.
This is one of my favourite parts of working iteratively, because the form of what you're making starts to crystallize, there's not much to add or remove, everything feels fresh, clean and known to be good. You also tend to be deleting a lot of code, and a lot of cruft falls off.
Also, quite importantly, a lot of time was spent on refocusing the short term goals for luxe. More on that later.
Here's some arbitrary statistics from the three main codebases for luxe since the last dev log (march 2019).
- module - 306 commits, 355 files changed
- runtime - 269 commits, 937 files changed
- editor - 203 commits, 121 files changed
If you're interested in the amount of code in the engine overall - keeping in mind that lines of code is not a very useful metric of anything - here are the statistics for the engine and module,
and the editor code stats.
luxe 2019 in context
Below we'll go over various parts of the engine and projects using it to see a slice of what went on during 2019.
Editor updates
The editor got a lot of important fixes this year, as it's an important complement to the 2D workflow for many users.
As always, editor UI is a constant work in progress,
what is shown is often a first pass and is likely going to change several times.
Projects being able to specify editor functionality was fixed up, projects can now render in editor how they would in game, and a bunch of fixes on the various individual editors were made. A few improvements for a some of the specific editors are shown below.
You can also deploy builds, preview and create assets in initial form and plenty more from inside the editor. It's starting to feel a lot more useful.
A lot of usability was improved as well. Switching between editors is now far more approachable, each editor can include a keymap which shows actions available, and can also include a short readme that helps familiarize yourself with each tool right there. A big goal with the editor is that you can learn a lot about using it through exploring, rather than having to read external documentation just to get started.
selecting a context
keymaps in the world editor
learning about the tile editor
material previews
in-editor rendering = game rendering
Tile editor updates
The tilemap editor saw several fixes and improvements for workflow and usefulness. It's pretty close to usable from the user facing perspective, which is a good place to be.
It will continue to improve and evolve, but you can use it to create reasonably sized levels without much friction. A few more improvements and I'd call it ready to use.
tile properties
rectangle selections
tags
Tiles can also be tagged, so systems can use those to perform filtering. This video shows the world editor, switching to the tile editor, editing the tags on a few tiles to solid
, and then switching back to see that there is now collision in the world there. In this case, the Actor
module mentioned below is making colliders for you.
tile tagging in motion
World editor updates
The world editor continues to improve as well. It is also fairly usable now in most important ways, I've used it on several projects without much friction (both 2D and 3D). There are a few key tools needing to be completed before it can be called ready. For example: editing prototypes (prefabs) is nearly done but not ready.
'Preview World', rapid iterating
If you preview the world you're editing, the systems and modifiers active in it will run so you can quickly iterate on in game content within the editor. In this case, changing the player max speed to see what feels best.
prototypes in the scene view
custom modifiers, provided by the Actor module
editing a 3D scene with project specific rendering
Anim editor updates
The animation editor is one of the newest tools and therefore still requires several iterations to find the right workflow.
For example the Sprite track below are outdated, the frames are better handled as discrete frames instead of being keyframe based (shown). It's moving along well, however, and should be usable soon for creating animation assets.
Curve editing
Sprite editing
Outdated sprite anim track editor, one iteration in finding a good workflow. A better one is being made where frames are edited, not keyframes. (sprite from Isometric Game Kit)
Module updates
In dev log #8, work on the high level modules was mentioned. Most of the later part of 2019 was spent on rounding out two of these modules, both action/physics related.
Note that the modules are WIP, so there's some naming inconsistency and untidy details, all expected while working out the details.
One of these modules was mentioned before, currently called Actor, which is a pixel perfect AABB physics and collision system for pixel art games.
The other is called Arcade and is using the luxe SAT APIs for polygonal collision, and adds physics on top, for 2D games (pixel art or otherwise). Tilman has been making it.
Both of these modules have some common features (intentionally) like support for triggers, and they come with a bunch of preset trigger responses, like teleporting to another entity, loading a scene, running a simple script, tagging/untagging the thing that entered the trigger and more.
They also include a few player controller types for 2D games, like a platformer controller and top down controller, each which control over the feel of the controller.
A benefit of including tools like these with the modules is that you can make a reasonable 2D game without necessarily needing to leave the editor. For a lot of people this is a big deal, and is important to help people at all stages of learning to make games achieve their goals.
You can place a player in the world, construct tilemaps, place them in levels alongside other sprites and entities, and then have triggers that load new areas as you move around. You can extend that with the simple script trigger and add custom behaviour easily.
Arcade platformer sample
This shows one-way colliders of different shapes, teleporting, and controlling the physics directly for buoyancy (custom forces in an area trigger)
Actor top down sample
This sample shows different trigger types, one that loads a new scene into the world (that area doesn't exist until you enter the trigger!), teleporting and custom scripted triggers (code shown below)
Adding a new Actor trigger
Arcade modifiers
Arcade Top down modifier
Actor modifiers
Actor Script Trigger
Render module updates
Whenever I've shown work in progress 3D rendering stuff, it's typically been part of a project, i.e the code and shaders live in the project level. This is how rendering works in luxe, where the project decides, so that you don't have e.g 3D rendering stuff in your 2D game if you don't need it. You only pay for what you use.
Recently I was working on two games that were using the same 3D render pipeline, so I finally moved all the rendering from this particular pipeline into a module, so both projects can just share the module.
This is part of the intended workflow: you can grab one of several render pipelines that suits your project needs as a module (or make your own). These modules can be made up of other modules, e.g a bloom module might expose a shareable API so that multiple pipeline modules can add bloom easily.
physically based rendering
lights and decals
physics test scene got a lot nicer looking suddenly...
In order to get this rendering module working nicely, I had to finally refactor several of the pending tasks on the rendering API and material systems in luxe. How images were sent to the shaders was inconsistent with how other inputs were sent, and the model of sending inputs was meant to allow variable update rates for material inputs.
As a concrete example, the shaders declare inputs as 'blocks', and you're able to use Material Input Blocks to send information about say, the environment. This means you can update shader inputs once a frame, once per view, and the more usual way of once per mesh.
This is nice too because a material basis can request a block as an input (like the environment), which is also specified as an input on the shader side, and use it without much effort. The environment system only has to set the values once, on the input block.
During 2019 I spent more than a little bit of time fixing up this particular rendering pipeline because I wanted to use it for some games I'm working on. While it's not necessarily first release related, I sometimes have to self indulge and work on what I feel like. And as always, it's super important to actually make stuff with the engine, I've found and fixed quite a lot of key bugs due to these projects.
Below is one of the projects which has terrain with material blending. I posted about the terrain workflow I'm using over here. This project has nav meshes and pathfinding over the terrain, and fun facts about this scene: it loads 3Gb of assets in 3 seconds, and contains 8.6 million triangles.
Working on this module included redoing the physically based rendering shaders (my older initial implementation was a mess so I redid it all). I also ended up adding the cascaded shadows into the module, exposing all the settings to the game side, and fixed a lot of bugs.
In this thread, I posted about a bug that I fixed in the lighting that was doing spooky things in a quadrant, shown below in a different form.
weird lighting bug
The game jam game
I also worked on a game jam, just under 2 weeks worth of spare time, ending in a 3 day game jam at work. This thread has a long breakdown of the things I was working on for this game, showing various aspects like converting 2D tilemaps to 3D spaces, and more.
I also posted a short post after the jam about the project and the goals behind the game.
You can see a quick rough video of the gameplay I got done during the jam below.
quick rough video of the jam game
Detours
It's early in a new year, a perfect time for some existential musing!
quantum versioning
When you're working on a project, there's always at least two versions of it. One that exists right now, and the one in your head, the idealized version. There's a version you wish existed, it just doesn't (yet). There's actually usually quite a few other versions too: the one you can make, the one you can make in a reasonable amount of time, the one that will get made, the one ... the list is quite long.
In practice, often you're working toward the idealized version, "the vision", while making incremental versions along the way. Each of these incremental versions has importance.
Which one of these is the real project, though? The one that exists now, or the one that doesn't (yet)? This is an interesting question to me...
What about the user perspective? If you believe the real version is the ideal one, then the user might take the version they can use as the real one. Pragmatically, the only version that is literally real is the one that exists and can be used... maybe from the user world view, they're right. But maybe from your world view, you're right too.
If you release a version of the project, you likely want to communicate this somehow. You have to convey to the user that what they're seeing is a subset, a slice of your vision. This is a non-trivial problem in a lot of cases!
You, as the creator of the project, have to come to terms with all of these versions. It's impossible to release any version if you don't, because reaching the ideal version is surprisingly rare.
development priority
Also when you're working on a project, you have to make decisions about what you're working on at any given time. This is one of the hardest parts.
It often involves picking a goal or a milestone to reach (a version of the project!), and then priority is decided by what matters most to achieving that goal. You work through the tasks that need to be done, and then you have to pick a new marker to move toward, which will hopefully lead you to the ideal version.
How you pick those goals is important too. You can choose to go wide, to go deep on a particular subset, or any number of approaches. This influences the priorities as well.
Time is a critical factor too. You might get pretty far over time, and then realize the approach you'd taken earlier isn't fitting anymore. Time changes things like the 'market', your availability, the project itself, and maybe even the vision. The vision could become clearer, or more focused, or if you're unfortunate, bigger and further out of reach.
Time also changes you, as well.
Refocus
One of the important things to do when working on a (big long term) project is to frequently evaluate what it is that you're working on, how you're deciding, and whether you're just going through the motions. During 2019 I spent a lot of time thinking about the approach that I was using to move forward, and whether that was still valid. It wasn't!
unrelated: A small prototype made in a very short time
For a long while the approach I was using to reason about what to work on was about going wide, get as much of the engine in place and usable as possible, that way we can just iteratively improve it. This was working fine due to the season I was in, and the nature of my spare time. 2019 changed what my spare time looks like, so I had to re-evaluate whether it still makes sense.
More importantly, I was trying to evaluate the concerns I had with getting to a preview release.
What I found is that going wide was causing me a lot of invisible stress. In a way, luxe has a really wide audience, all the way from people who are making their first game ever up to people like me, who are writing their own rendering pipelines, have higher tech needs and need access to advanced things that will take more time to finish.
What this means in practice, when looking at it through a 'going wide' lens, is that even if there was a full featured, perfectly usable and friendly 2D engine ready to go, it would be blocked by the other user types.
This obviously makes little sense. Which is why we refocus.
New Focus
The new focus is narrow, on the preview release.
Instead of all the hidden pressures and concerns from future tools and implementation details that aren't ready yet, we're focused entirely on getting to a release.
Concretely that means finishing all the key systems for 2D games, getting the 2D editor tools usable, and working on the common 2D modules that allow rapidly making reasonably sized 2D games. And quite critically, making games too.
If you scroll back, you'll notice that the progress made during 2019 was explicitly about this! We've made great strides toward this goal and I'm hopeful for the outcome during 2020.
Expectations
This might mean some things you're hoping to use aren't ready at release (which was gonna happen anyway!), but it's important that people's expectations are aligned.
And more importantly, that these choices will be intentional and explicit, so that the first release of luxe is what I want it to be, and what it needs to be.
2020 and beyond
There's a really nice, solid, easy to use 2D engine with a lot of tools and editors just itching to get out there, and I know there's a lot of people waiting patiently for it.
I'm looking forward to getting to that point.
Whew
Since we're almost at 4000 words here, I'm calling this post done. I'm (still) hoping to post more frequently, I've got 2 tech posts lined up that I'll be revising soon. Till then!
Community
Read about the community conduct.
Join us on the luxe discord.
We run polls, answer questions, post in progress details, and talk about the engine.
News in other forms
You can follow news about luxe on twitter @luxeengine or by subscribing to the rss feed or using the mailing list sign up on https://luxeengine.com/. Especially I'd highly recommend signing up for the mailing list.
Feedback and questions on this post can be found below, or over email, or via twitter (@luxeengine or @ruby0x1).