Hello friends! As we start to wrap up for wider release we've got plenty to talk about. In the next few posts we're gonna explore what it feels like and looks like to use luxe to make games, in concrete detail, so get a cozy seat and we'll talk about it.
Please keep in mind all images and details are work in progress/not final ✨
Before we dive into the details, here's some shiny things to mention.
Brick & Bramble
This is a game created in luxe that recently released their prototype build for feedback and community but it's great to see more games being made in luxe by other devs/studios!
This one has a lot of cool procedural stuff going on and is a relaxing experience being made by good people. If that interests you they'd love to hear about your experience as they develop it, and if you're interested in their dev process they post about it often in their discord.
The header image shows the game but here's a few more images (not final of course)!
Mossfield Archives
As luxe is made by a game studio we make games! Sometimes multiple games!
Our first game Mossfield Origins was preparing to ship after some delays, but we announced our second game earlier this year and shared a teaser. The game is called Mossfield Archives and is a more elaborate 3D game created with luxe. We're making it with our friends at Studio Pomidori.
We'll be sharing more about that in the near future but in the meantime enjoy the teaser! You can see the visuals in motion at the link above, and you can wishlist + follow on Steam.
Untitled game
Another luxe user is making their own rendering pipeline tech, with a custom compute lighting pipeline (a unified dynamic lighting system with realtime GI), procedural foliage/mesh instancing system, and more.
It's very specialized to their project but it's always good to see people pushing the tech! This is a tiny early sneak peek from their prototype but you can read a recent post on the cloud rendering over here and more on their blog.
luxe 102 - what is like to use luxe?
Over on dev log #9 we spoke about luxe 101, the core concepts of working in luxe. Today we're gonna take a deeper look at what typical game code looks like in a luxe game and how it works.
We also did the luxe jam a while back, where people shared their experiences using luxe on a much older build. Now that things are significantly nicer and further along, we can share how it looks in concrete details.
Below is an expandable section if you'd like a recap of the core concepts!
luxe 101 recap
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!
Show me!
Before we write a lot about what they are and how they work, let's start with showing how they feel to use. I think many people will already be familiar with how they feel to use.
Let's use a canonical example of a fundamental piece of making a 2D game - the Transform
modifier and Sprite
modifier.
If we want to place an object in our game world, at a location, we 1. create a blank entity 2. attach a Transform
modifier. We can attach a sprite to display an image at that location.
In editor...
First we'll see how it works in editor, as this is a very common workflow (but not the only one in luxe!).
- This shows foundations of a 2D game: A sprite we can move around
- Keep in mind the editor is optional (you can use a code first/only workflow)
- Frequent combos can use templates (e.g one click for a
Sprite
+Transform
+Tags
ready to use)
In code...
In code we can do the exact same thing, but we do it using the modifier APIs, like this:
There's a consistent pattern of create -> configure, but sometimes modifiers add convenience create
methods, for example the following is possible too:
It's typically up to the modifier itself what conveniences it provides, but by convention all modifiers respond to a consistent API with create
, has
and destroy
among other things.
They can also be used by type id, allowing generic access, like Transform
is known as luxe: system/transform.modifier
and luxe: system/sprite.modifier
for Sprite
.
All modifiers have this kind of id, as they're declared in code (we'll see that in the next dev log in detail). We can speak about them without knowing the type - this is how the editor works - it just displays the data for that system by id instead of knowing about all their classes.
Provided modifiers
The luxe module comes with several provided modifiers that are a strong foundation ready to make games with (mentioned in the 101 part) - so what are some examples for 2D games?
(note: there are more + many for 3D specifics not shown)
Transform
- position/rotation/scale, allows linking transforms togetherSprite
- displays an image or custom materialText
- displays text in world spaceAnim
- plays animation tracksCamera
- stores camera propertiesSound
- 2D or 3D sound instancesTags
- Tag an entity to find groups or individual ones easilyTiles
- Display a tiles asset in the world
We won't be able to go through all the details of the provided ones, some are simple some are pretty extensive. Let's look at two very common ones - Sprite
and Text
to show what we mean.
Ready to use?
What does "ready to make games" mean to us? The way we think about it is "the 99% use case" or common case.
99% of 2D games will probably have a Sprite, and a very common case is that a game be rendered using pixel art. So, naturally, we want to make it really easy to use that common case - we have a pixelated
flag. Check the box, it displays as pixel art (you can configure defaults too).
Sprite
Here's what the modifier shows in the editor for the luxe sprite modifier (this gives you a clear view for most of the API as well, typically API has more flexibility on top).
Here we can see obvious stuff, like size, color, flip and so on but there's more to show.
The sprite modifier defines a sort of 'protocol' around what a sprite implementation should provide. This includes expectations and data that is available to a shader, and we provide a default shader that implements them all. You can of course use custom shaders, which may or may not use that information.
What about the fold out sections?
billboard
First we have billboards - a common case! We often like to display a sprite in 3D space, have it face the camera at all times. Or maybe only around the Y axis, so more like a cylindrical billboard instead of a spherical one. We commonly want to display them in world units (like wolf3d style) or with fixed size (editor gizmo) or with a fixed screen size (legible in game icons at any resolution).
atlas
Next we have atlas options, another common case.
In our game Mossfield Origins we have a set of character poses to display, and it's convenient to reference them from an atlas instead of individual images, so we create an atlas asset that packs them into the atlas and then reference them by an id within it and with the same size (without trimming/rotating inside the atlas).
The atlas asset type has uses outside of sprite, it's a Service (see luxe 101), but sprite uses the service to add this common case.
custom material
This one is self describing: if you have custom shaders for a sprite, you can assign a material override.
By default sprite handles the common case + implements the protocol for a wide range of uses, but there's many cases where we need more, and here's where you set that. Your custom material will be sent the info to use as needed, and you can bring in built in functions in the shader as needed.
advanced
Maybe this category is named oddly right now, so this will probably get cleaned up but here's what this category exposes...
HSV
Often there's a need to modify the colors of a sprite a little without resorting to a full custom material - a common case would be team colors on a sprite or user customizable clothing colors. If you want to tint a sprite while preserving it's color (not multiply) you can use the hue change field (in degrees) to shift the hue.
You can also boost or lower the saturation (making it black and white) and you can change the value which changes how dark things are. Here's a quick example to illustrate:
Outline + Shadow
These are pretty clear and a common case. Here's a quick example of an outline + a shadow. The sprite itself has pretty sketchy edges (drawn intentionally that way) but still gets a nice effect, like a sticker or making it pop off a background. This also works when pixelated
is true - you get clean pixel perfect outlines or shadows.
shine and dissolve
These are animated effects, so let's see them in motion.
With just a few settings, we can get a wide variety of behaviours from the shine effect, especially useful for feedback to the player of a game, such as charging up, healing, collectibles and so much more. This acts like a line that sweeps across the sprite over time, but we can control how it looks.
Then there's dissolve, which will mask the sprite against a dissolve image you specify, typically useful for gradual reveals or transitions out.
Text
Text is crucial to games, whether displayed in world in the UI modifier, we definitely want to have a rich set of tools to use. Note that text is a hard problem and we're not finished with everything we want to add. We strongly believe in good multilingual support and robust tools though and work towards them.
There's a lot of things that come with text no matter where it's used (world, ui, debug), for example:
Localization support
with realtime updates. e.g if you switch language, it can refresh to display the new language. Backed by the localization tools in luxe using PO/POT format, and includes tools like length randomization to find strings that might escape your spacing with longer languages.
It also supports scrolling if the text becomes too long like a marquee effect, allowing fixed sized labels that are still readable with size changes or language changes.
Styles
Multiple fonts, sizes, colors and styles in the same text. Includes outline + shadow effects. Text style assets for sharing styles across a game. Some of you may remember this really old image! This showed the mixing of colors and fonts initially - we've come a long way since!
Text now uses the latest MTSDF variant which gives really clean scaling and works for a wide range of cases via MSDF, as well as a regular SDF for things like outlines or drop shadow effects.
What does MTSDF mean?
SDF stands for Signed Distance Fields, it's a common way to display fonts as they can scale well from a small image. MSDF is "Multi-channel Signed Distance Fields" which added a lot more resolution and quality to the idea, and the T in MTSDF is for 'true' - it adds a regular SDF into the alpha channel of the image. This allows really clean text rendering, as well as soft effects like shadows or outlines!
Here's a rough example from our game that shows some examples in practice:
This is what a current (not final) text style asset contains for interest sake, which gives an idea of how it applies:
Inline mark up syntax
The first tier of helpful support is basic markdown syntax with configurable defaults. e.g When you type # This
you get bigger text, single *
will give italic, two will do bold. This uses text styles or defaults to allow rapid iteration and consistency while working, in so many cases we just want to emphasize a bit.
There's more to it though, as you can also be more explicit, and write hello [there](color=#ff0000 font='font/example' size=20)
for extra control. These tags provide some built in ones, but allow custom expansion in future.
inline icon fonts
For displaying icons inline, a very common case, there's the $icon
syntax.
This allows adding your own game specific ones (or from a module) that you can add like $food %(food_amount)
or localized ones via {0}% $research
.
This works well with platform specific button prompts like $interact
which can switch based on user settings or platform detection - which we'll be providing a bunch of platforms ready to throw in for initial development.
We include a wide number of icons by default currently for prototyping and debugging (that you can exclude of course, and we're still settling on a final list), so while prototyping you can do stuff like $camera
or $food-apple
and get moving quickly. Below shows a built in $camera
and a game provided $github
inside a string.
Tools like these allow getting a solid feeling experience implemented quickly so you can focus on making the game part.
Next time .... making your own modifiers!
As the 101 part mentions, we can never provide every possible system, so you need to create your own to make the game part or to provide tools to other users. In the next post we show how implementing a modifier works in luxe.
Part 1: fin
Since this post is now almost 3000 words, we'll continue in the next post. This was part 1 of a few so keep reading for more details on what it's like to use luxe in practice!
Get the latest news
All posts in this series: