update rules

This commit is contained in:
Joe 2026-02-02 14:05:11 -06:00
parent 4c390fdd37
commit d5522b2fef
8549 changed files with 11783 additions and 1888 deletions

View file

@ -0,0 +1,186 @@
---
title: Building architecture
description: Explains the design concepts for architecture in Beyond The Dark.
next: /resources/beyond-the-dark/custom-characters
prev: /resources/beyond-the-dark
---
Building the station in a modular way allowed us to update and propagate content updates smoothly and it was key to making sure we didn't spend hours updating individual meshes, textures, parts, and more.
Roblox packages let us achieve this modular design by letting us package together a palette of `Class.MeshPart|Meshparts` that easily snapped together, trim sheet sets, and `Class.SurfaceAppearance` nodes that the `Class.MeshPart|MeshParts` could share. This let us adapt to changes in layout and size during early playtests and made the process of filling out the station much faster.
<img
alt="Architecture Overview"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture-Overview.jpeg"
width="100%" />
## Flexible designs to final art
When we first started, we didn't have complete understanding of what we wanted to be in the station, or how big it needed to be for 50 players.
To figure that out, we started first building out the environment using `Class.Part|Parts` and bringing in some meshes like the rotunda shape from our external DCC application, such as Maya or Blender. With these simple shapes, we adjusted the scale of the map, arrangement of the rooms, and pathways to find the most fun arrangements. Whether for a game or a social experience, this early stage tends to be the most dynamic.
When building the layout of the station, the most important thing to drive exploration was to give the user a goal. In this case, the most obvious goal is to get a closer look at the black hole. Next, we knew giving them a straight path to the black hole would be boring, because a player can just walk from spawn to the end of the map. We broke the path so the player couldn't get to the black hole without first going around and seeing other things that might draw them in too.
The modular design allowed us to make such changes to the environment without catastrophic reworking of the overall design. We could move entire sections of the station to play with different gameplay ideas. Ultimately, these same modular pieces could then be updated using packages so we didn't have to rebuild multiple versions of the same asset.
<img
alt="Architecture Example 1"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_1.jpeg"
width="80%" />
<img
alt="Architecture Example 2"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_2.jpeg"
width="80%" />
<img
alt="Architecture Example 3"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_3.jpeg"
width="80%" />
<img
alt="Architecture Example 4"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_4.jpeg"
width="80%" />
## Modular design of Kerr-Newman DSR-14
The space station uses just a handful of modular palette pieces. Modular palette pieces are reusable assets, meaning we don't have to create each wall uniquely but instead can put together several smaller walls. This allows flexibility in layout as we're building.
Here are some tips that helped us create our modular design:
- **Settle on a uniform grid size** — For the station, we settled on a 16 stud grid size to create most of the modular set. The grid size you use is arbitrary, but should be consistent throughout the project and across all artists working on that project. Settling on a grid size at the beginning of your project ensures each piece fits nicely with the piece next to it. It also allows you to swap pieces easily, for example swapping a wall section for a door section.
- **All units are imported as studs** — When creating meshes in other DCC applications, such as Maya or Blender, Studio doesn't convert meters, centimeters, feet, or inches — it simply replaces the incoming units with the same units in studs. For example, if you set your units to meters, Studio converts 2 meters to 2 studs. It's best to treat your unit scales in your application of choice as stud scales in Roblox. The closest real-world scale for a single stud in Roblox is 28 centimeters.
- **Keep pieces simple** — Don't try to build out every piece that you think you'll ever need. The idea is to have a few very versatile pieces and not a lot of one-offs. The more pieces you have, the more time it takes to create each piece, and the more it affects your overall memory and performance budget. You can always add another piece to your kit if it's absolutely needed.
- **Use packages** — Convert every modular piece to a package and also put materials in packages before you start populating them in your experience. This lets you easily push updates to all instances. We'll dive into packages a bit later on.
The following screenshots show you various ways we used modular design to construct portions of the station.
<img
alt="Architecture Example 5"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_5.png"
width="80%" />
<img
alt="Architecture Example 6"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_6.png"
width="80%" />
<img
alt="Architecture Example 7"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_7.png"
width="80%" />
<img
alt="Architecture Example 8"
src="../../assets/resources/beyond-the-dark/building-architecture/Architecture_Example_8.png"
width="80%" />
## Create trim sheets
1:1 textures are the way of the future, but they become costly in regards to memory budgets and time to create. For the space station, 90% of the architectural elements used a handful of swappable trim sheet sets, which are a set of texture maps that are laid out in either a vertical or horizontal fashion. Each row or column of the sheet has a unique look to it, giving you a bunch of different surface treatments to choose from when unwrapping a model, all while using one image sheet.
Here's some tips for making trim sheets:
- **Start with an inventory of the type of objects and materials you need** — Trim sheets can include a large variation in material types, such as wood, metal, rubber, and more. Before you start to model your first object, create a trim sheet template. This is the starting point for all your other trim sheet sets and it gives you a quick starting point for unwrapping your models. These trim sheets live within the `Class.SurfaceAppearance` node, which we'll cover later.
- **Include a variety of surface treatments in one sheet** — This lets you keep more of the mesh together, so you can apply new treatments without having to break your meshes into multiple meshes. This improves runtime performance by decreasing object and material draw calls.
- **Make a trim sheet template** — A template ensures that you can swap out any map set for another. For example, you can swap a clean metal set with a rusted metal set.
Here's how we created trim sheets for the station:
We created a rough outline of what the sheet should look like:
<img
alt="Trim Sheet Example"
src="../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Example.png"
width="60%" />
In Photoshop, we created a mockup to get a quick idea of what each trim section of the sheet would look like.
<img
alt="Trim Section Example"
src="../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Example_2.png"
width="60%" />
We used Substance, but you can use whatever application suits you to create the final maps. This is the final set of trim sheet maps that were generated in Substance. All four maps work together within a SurfaceAppearance node to produce a Physically Based Rendered (PBR) material.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/building-architecture/PBR_Example_1.png" />
<img src="../../assets/resources/beyond-the-dark/building-architecture/PBR_Example_2.png" />
</GridContainer>
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/building-architecture/PBR_Example_3.png" />
<img src="../../assets/resources/beyond-the-dark/building-architecture/PBR_Example_4.png" />
</GridContainer>
Here are some final renders of the trim sheet inside of Substance Designer:
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Final_1.png" />
<img src="../../assets/resources/beyond-the-dark/building-architecture/Trim_Sheet_Final_2.png" />
</GridContainer>
And here are examples of the same trim sheets being used on a large range of objects:
<img
alt="Modular Trim Sheets"
src="../../assets/resources/beyond-the-dark/building-architecture/Modular_Trim_Sheet.png"
width="80%" />
If you want to learn more about Trim Sheet UV Mapping, just search the topic on the web; there are many great tutorials out there.
## Apply surface appearances
`Class.SurfaceAppearance` lets you specify new texture inputs to our Physically Based Rendering system for `Class.MeshPart|MeshParts` and lets you override the appearance of a mesh with advanced graphics options, including ColorMap, MetalnessMap, RoughnessMap, and NormalMap. It also makes use of the new dynamic cubemap system that Roblox uses for reflections in materials, such as chrome. For example, here's a side-by-side image of a mesh with SurfaceAppearance applied next to a mesh without it:
<img
alt="Surface Appearance Example"
src="../../assets/resources/beyond-the-dark/building-architecture/Surface_Appearance_Example_1.png"
width="80%" />
When using surface appearance, keep the following tips in mind:
- If your trim maps contain alpha information for transparency, change the AlphaMode from Overlay to Transparency.
- Surface appearance doesn't work with Parts because they don't have the UV information required to apply the maps with. Parts use world-aligned projections in place of UV information. MeshParts are normally created in another app such as Maya, 3ds Max, or Blender and contain UV unwrap information.
To apply `Class.SurfaceAppearance`:
1. Click on the at the end of a `Class.MeshPart` or folder name in the **Explorer** and search for `Class.SurfaceAppearance`. Adding one will override any BrickColor, Color, Material, and TextureID you may have already applied.
2. Rename the `Class.SurfaceAppearance` node to something unique and add a postfix of `_SA` to make them easy to search for. In this example, we used `Metal_Trim_A_SA`.
3. Import the trim sheet maps, copy the IDs from the **Asset Manager** window, and paste them into the appropriate slots in the **Properties** window. Here's how the `Metal_Trim_A_SA` node looks with all the map IDs inserted into and applied to the door.
<img
alt="Surface Appearance Example 2"
src="../../assets/resources/beyond-the-dark/building-architecture/Surface_Appearance_Example_2.png"
width="320" />
<Alert severity="info">
For more information on Physically Based Rendering (PBR), see Adobe's Substance Designer documentation [Part 1](https://substance3d.adobe.com/tutorials/courses/the-pbr-guide-part-1) and [Part 2](https://substance3d.adobe.com/tutorials/courses/the-pbr-guide-part-2).
</Alert>
## Increase efficiency with packages
Packages let you create object instances throughout your Roblox experiences, so that updates you make to the package are published to all instances of that package. We used packages for almost every object we put into the station, so we didn't have to hunt down each placement if we needed to make tweaks. For example, we used over 1,000 wall panel instances of a package and were able to publish updates to them instantly by modifying the package.
Follow these tips when using packages:
Turn on the auto-update feature in `Class.PackageLink`. All modifications are applied to all uses of the package when this is enabled, saving you the step of having to use the "update all" feature.
<img
alt="Packages Example"
src="../../assets/resources/beyond-the-dark/building-architecture/Packages_Example_1.png"
width="320" />
You can use packages for `Class.SurfaceAppearance` objects to create a "material library." This lets you modify bitmaps in a `Class.SurfaceAppearance` object and publish them out to every object that uses it. The following screenshot shows master materials used throughout the demo. If we needed to modify the maps, we just had to update the master material and republish the package.
<img
alt="Packages Example 2"
src="../../assets/resources/beyond-the-dark/building-architecture/Packages_Example_2.png"
width="80%" />

View file

@ -0,0 +1,320 @@
---
title: Custom characters
comments: |
1. Needs linking to Rigging / Skinning section in External Modeling.
2. Should also link to Modeling Requirements in end of Rigging section.
description: Explains the design concepts and systems for characters in Beyond The Dark.
next: /resources/beyond-the-dark/layered-clothing
prev: /resources/beyond-the-dark/building-architecture
---
You can import custom meshes to create everything from NPCs to an animated cloth of a sail boat. We used the custom setting of the [3D Importer](../../art/modeling/3d-importer.md) to bring in more exotic custom characters, like the mysterious black hole creatures and the friendly, if unaware, service droids.
The following sections go over how we used [rigging and skinning](../../art/modeling/rigging.md), [PBR (surface
appearance)](/art/modeling/surface-appearance), and VFX to build one of our more complicated characters that we named the "Creature." We wanted it to glow, emit some light, have trails of particle smoke, and a fluid motion that involved skinning a rig with enough joints to create the convincing waves of its tentacles.
<img
alt="Creature Banner"
src="../../assets/resources/beyond-the-dark/custom-characters/Creature_Banner.png"
width="100%" />
## Rig
When we were rigging the Creature, we found it best to model the character in a neutral pose, because that pose is best suited to bending in multiple directions. If we modeled the Creature with its tentacles already curled, it would have led to stretching if we animated the tentacles to bend in the opposite direction. The following screenshots show the Creature in its natural state:
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/custom-characters/Rigging_Neutral_1.png" />
<img src="../../assets/resources/beyond-the-dark/custom-characters/Rigging_Neutral_2.png" />
</GridContainer>
From a neutral pose, we added joints economically, focusing on the areas that needed the most movement. The fewer joints the better, because you'll have to manage them when skinning your character, and you'll have less to control when you animate them. In the previous screenshots, the Creature looks like it has a lot of joints in the center, but the main body only has one joint.
Most of the other joints are towards the tentacles and mandibles. With the tentacles, we wanted a lot of secondary motion, or motion that is layered, to create a convincing effect that they're all moving on their own. However, the center mass of tentacles were so close together that it felt wasteful to make joints for all of them, both on performance and effort to animate. So instead, we treated the center mass as a single large tentacle with smaller "tails" where the tips left the center mass.
<img
alt="Creature Front, Maya"
src="../../assets/resources/beyond-the-dark/custom-characters/Creature_Front.png"
width="70%" />
<Alert severity="info">
It sometimes helps to make a "control rig" that saves you from having to animate every joint by hand. You can search for excellent tutorials of control rigs appropriate for your character and corresponding DCC application.
</Alert>
We found the following guidelines useful, so that the character mesh imports correctly into Studio:
- Individual joints and bones must have unique names.
- Meshes must not have the same name as joints and bones.
- Meshes should have no transforms prior to skinning/binding; in other words, transforms should be 0 and scales should be 1.
- Mesh normals should face outward (the model shouldn't look inside-out).
- Skeletons shouldn't have any scale factor; all joints should be [1, 1, 1].
## Skin
When we finished the Creature's skeleton, the next step was to skin the mesh. Skinning can be an arduous task, so to make matters easier, it's best to be familiar with the different initial skinning settings of your DCC application to find the one you like.
Since this is an organic character, we skinned it with plenty of falloff on each joint and overlap between them. This way, the bending feels smooth and not sharp. The following screenshots show bad skinning and smooth skinning respectively:
<GridContainer numColumns="2">
<figure>
<img src="../../assets/resources/beyond-the-dark/custom-characters/Bad_Skinning_Example.png" />
<figcaption>Bad Skinning</figcaption>
</figure>
<figure>
<img src="../../assets/resources/beyond-the-dark/custom-characters/Smooth_Skinning_Example.png" />
<figcaption>Smooth Skinning</figcaption>
</figure>
</GridContainer>
We found the following guidelines produced the best outcomes for skinning:
- Skinning influences (meaning they affect a part of the model when moved) should be a maximum of 4 influences per vertex.
- Joint and mesh names need to be unique, both within each other and between each other.
- Any joint you want to import into Studio must have some influence on the model's skinning, otherwise the engine doesn't import it.
Whenever possible, skin your model in its original or "bind" pose.
## Import the mesh to Studio
Importing your custom characters into Studio is one of the more exciting parts of the process, because you get to see your creations in the experience you're building!
To import the mesh into Studio:
1. Export the character from the DCC application and ensure the following:
- All normals, scales, and names are correct.
- The character has all joint and bone hierarchy and all meshes.
- Meshes are all under 10,000 triangles for each part of the mesh.
- Mesh total size isn't over 2000 units in any axis.
- See [Mesh Requirements](../../art/characters/specifications.md) for a complete list of model specifications.
<img
alt="Creature in DCC Tool"
src="../../assets/resources/beyond-the-dark/custom-characters/Creature-In-Maya.png"
width="80%" />
2. In the [3D Importer](../../art/modeling/3d-importer.md), import the custom `.fbx` or `.obj` file.
<img
alt="Creature Imported Into Studio"
src="../../assets/resources/beyond-the-dark/custom-characters/Custom-Import-2.png"
width="80%" />
## Make the creature glow
Once the Creature's model was stable and didn't require any more immediate import into Studio, we started putting together the SurfaceAppearance objects, lights, and visual effects. We did this to ensure that the quality of the model was good enough before proceeding to place and edit any one aspect of it.
<img
alt="Glowing Creature Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Glowing-Creature.png"
width="80%" />
We knew we wanted the Creature to be dark, and the focal points to be the eyes and its "grabby" tentacles. High points of contrast tend to attract attention, so having a few strong ones ensure the viewer knows what to focus on. Studio supports neon materials that self-illuminate, so early on we separated the eyes out so they could be their own material from the rest of the character. We did something similar for the tentacles, so they would glow on their tips only.
<img
alt="Creature Eyes in Studio"
src="../../assets/resources/beyond-the-dark/custom-characters/Creature-Eyes.jpeg"
width="80%" />
Neon material doesn't emit actual light, so after some testing, we added separate parts to control placement and direction of light emission. This ensured the lighting was directed in a manner to enhance the glow of the eyes and also project its own source of light.
<img
alt="Point Light in Studio"
src="../../assets/resources/beyond-the-dark/custom-characters/Light-Emission.jpeg"
width="80%" />
Notice that the `SpotLights` add visual flare to the Creature close to other surfaces or a player.
<img
alt="Creature Light Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Spotlights.png"
width="80%" />
In addition, we wanted the Creature's tentacles to emit some particles, so it would leave behind a smoke trail when it moved. Because the tentacles are very long, adding the `Class.ParticleEmitter` to the entire tentacle would make the particles emit from the entire tentacle instead of the tip. To combat this, we used a small part positioned near the end of the tentacle, so we could control the emission size, placement, and direction of the particles.
<img
alt="Particle Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Particle-Emitter.jpeg"
width="80%" />
## Make the VFX follow the character
The skinned character's mesh positions aren't updated when the Creature animates, so we needed a method to make sure the VFX, SFX, and lights all followed the Creature properly. To accomplish this, we created a VFX controller script and used CollectionService to inform the parts that contained the VFX where the creature's bones were and to follow them.
1. We placed the following `Class.LocalScript` in **StarterPlayer** → **StarterPlayerScripts**. This essentially runs the VFX update function.
```lua title="Local Script"
-- Add this snippet to an existing local script that makes PreSimulation
-- connections
local RunService = game:GetService("RunService")
local vfx = require(workspace.VfxUpdateModule)
RunService.PreSimulation:Connect(vfx.updateVfx)
```
```lua title="Module Script"
-- This module attached parts to animations so they are updated as the
-- animation plays. It is a workaround for the current limitations
-- with Joints and Bones and will not always be necessary.
--
-- Prereqs:
-- To be included, models need the "AnimatedVfxModel" tag, and a folder
-- of all parts that you want to sync with animation. Each part needs
-- an attribute called "AttachedBoneName" that refers to the name of the
-- bone you want to attach to. Parts should also already be in their
-- correct positions relative to the desired bone.
--
-- To Use:
-- A LocalScript should require this module, then connect
-- VfxUpdateModule.updateVfx to the RunService.PreSimulation event.
local VfxUpdateModule = {}
local CollectionService = game:GetService("CollectionService")
-- SETUP - this should run once on every client.
-- Collect all models with the tag
local vfxModels = CollectionService:GetTagged("AnimatedVfxModel")
local vfxTable = {} -- where we will store all the parts and offsets
-- Assign a table to each model that will hold all vfx parts and offset
for _, model in vfxModels do
vfxTable[model] = {}
local vfxParts = model:FindFirstChild("VFX"):GetChildren() -- Find theVFX folder
-- Find the bone via attribute and calculate the offset for each part.
for _,part in vfxParts do
local name = part:GetAttribute("AttachedBoneName")
local bone = model:FindFirstChild(name, true)
if bone then
local offset = (bone.TransformedWorldCFrame:inverse() * part.CFrame)
vfxTable[model][part] = {bone, offset}
else
warn("Vfx part refers to bone that could not be found.")
end
end
end
print(vfxTable)
-- UPDATE - This should be linked to every client's RunService.PreSimulation
-- Go through all models, then update all parts on the model to match the bonecframe.
function VfxUpdateModule.updateVfx()
for model, vfxParts in vfxTable do
for part, bone in vfxParts do
part.CFrame = bone[1].TransformedWorldCFrame * bone[2]
end
end
end
return VfxUpdateModule
```
2. We created a **VFXUpdateModule** `Class.ModuleScript` to tell any objects tagged appropriately with **AnimatedVfxModel** to update on a play event.
3. We tagged the necessary model groups with **AnimatedVfxModel** using the [Tags](../../studio/properties.md#instance-tags) section of its properties. Using tags allows the **VFXUpdateModule** to know which object to look for as the first VFX child and to apply the update.
<img
alt="VFX In Studio Example"
src="../../assets/resources/beyond-the-dark/custom-characters/In-Studio-VFX.jpeg"
width="100%" />
4. Finally, we added an **AttachedBoneName** custom attribute to the part we wanted to animate and added the precise name of the joint we wanted it to follow.
## Texture the creature
Next, we set up the PBR (Physically Based Rendered) texture maps. These powerful bitmaps give the creature the varied sheen and surface variations to make it look like it has a lot of small bumps and imperfections. This visual effect helps sell the appearance of the Creature when it's closer to the player.
<img
alt="Creature Texturing Comparison"
src="../../assets/resources/beyond-the-dark/custom-characters/Surface-Appearance.png"
width="100%" />
Here's how we created the surface appearance texture maps:
1. The texture maps for this character were all on one "sheet" per map. This made the creature more efficient and meant we had to deal with fewer texture maps or `Class.SurfaceAppearance` objects.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/custom-characters/Texture-Map-1.jpeg" />
<img src="../../assets/resources/beyond-the-dark/custom-characters/Texture-Map-2.jpeg" />
</GridContainer>
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/custom-characters/Texture-Map-3.jpeg" />
<img src="../../assets/resources/beyond-the-dark/custom-characters/Texture-Map-4.jpeg" />
</GridContainer>
2. For areas that needed to glow or be self-illuminated, such as the "grabby tentacles," we also used transparency on the `Class.SurfaceAppearance` to blend with those parts.
<img
alt="Illumination Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Surface-Appearance-Transparency.jpeg"
width="80%" />
We found it helpful to follow these guidelines when creating surface appearance texture maps:
- Make sure your maps are no bigger than 1024×1024.
- Your green channel may need to be flipped depending on the application you worked in.
## Animate the creature
Animating is very subjective and has a personal style. Options include motion capture, hand "key frame" animating in your DCC application, or using Studio's powerful **Animation Editor**.
As we mentioned earlier, we wanted to make sure we had enough joints for fluid motion as well as enough limbs, so the creature animation felt natural and "layered." Layering, also known as secondary motion, is something you see in everyday life — when you throw your arm out, every joint is reacting to the initial impulse from your upper arm, and every joint in your body doesn't move or rest at the same time.
We used this technique to animate the Creature to feel like the limbs were reacting to the movement driven by its body as shown here:
<img
alt="Creature Animation Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Animation-Example.gif"
width="100%" />
If using an external DCC application for animation, we found the following guidelines work best:
- Set frame rates to at least 30 FPS.
- Create your animations with the same character you're applying it to.
- Export your animation data as a separate `.fbx` file.
Because we animated the character outside of Studio, we needed to use the **Animation Editor** to import the animation `.fbx` file. The editor lets you select any avatar with joints or motors and drive them via points in the timeline.
To import an animation:
1. In the toolbar's **Avatar** tab, click **Animation**.
2. Select the rigged character that you want to animate in Roblox. The character should be the same one you are rigging in your external DCC application.
3. Click the **&ctdot;** button in the upper-left section of the editor window, select **Import From FBX Animation**, and locate your exported `.fbx` animation file.
<img
alt="Animation Import Example 2"
src="../../assets/resources/beyond-the-dark/custom-characters/Animation-Import-2.png"
width="80%" />
<Alert severity="info">
If you animated at a different frame rate than 30 FPS, you may get a warning about alignment issues. This is because the key frames at different frame rates will not align to the frame count in Studio. You can generate keys at those points in time, or ignore it. Either way you'll be able to export, but aligning allows you better control over editing the frames in Studio.
</Alert>
4. When you're happy with your animation, you can export it to discover the animation ID, which you can then use in Roblox scripts to run them. For example, you can add a `Class.Script` to the model group of the imported character and use the following code to run the animation:
<img
alt="Animation Script Example"
src="../../assets/resources/beyond-the-dark/custom-characters/Script-Example.jpeg"
width="80%" />
```lua
local animationId = "YOUR_ANIMATION_ID"
local char = script.Parent
local animController = char:FindFirstChildWhichIsA("Humanoid") or char:FindFirstChildOfClass("AnimationController")
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://" .. tostring(animationId)
local animTrack = animController:LoadAnimation(animation)
animTrack:Play(0, 1, 1)
```
<Alert severity="warning">
Check the character after you've closed the **Animation Editor** to ensure that the **AnimSaves** folder was deleted, as its files are only useful during editing.
</Alert>
## Final results
After a few finishing tweaks to colors, light brightnesses, and some more particle effects to give it a stronger halo effect in front of windows, here's the final result in the space station!
<img
alt="Final Creature Rendering"
src="../../assets/resources/beyond-the-dark/custom-characters/Final-Result.png"
width="80%" />

View file

@ -0,0 +1,26 @@
---
title: Beyond the Dark
description: Beyond the Dark is a showcase demo with accompanying documentation on the concepts and processes used during development.
next: /resources/beyond-the-dark/building-architecture
---
<Alert severity="warning">
The following project notes were written and published at the release of the Beyond The Dark showcase. Processes and features may have changed since the time this was written. Refer to the appropriate feature documentation for up-to-date information on any features and workflows.
</Alert>
<img
alt="Beyond The Dark Banner"
src="../../assets/resources/beyond-the-dark/btd-banner.png"
width="100%" />
[Beyond the Dark](https://www.roblox.com/games/7208091524/Beyond-the-Dark-Vistech-Showcase) is an official Roblox experience used to showcase Studio's various features and tools to create a high-fidelity environment. Explore the Kerr-Newman DSR-14 space station in this official demo, non-copylocked and editable in Studio.
This document goes over how we put the most important parts of the Kerr-Newman DSR-14 space station together, providing best practices along the way as well as describing the systems used to create the sights and sounds of the station.
You can follow the articles sequentially or jump between the following sections:
- [Building architecture](../../resources/beyond-the-dark/building-architecture.md) explains how to build content in a modular way. This allows you to go from early layouts to a polished experience in the fastest way possible.
- [Custom characters](../../resources/beyond-the-dark/custom-characters.md) explains how we built the creepy creatures in the station and best practices for building your skinned custom avatars to import into Studio. It also covers some tips for adding visual effects, sound effects, and lights to the character.
- [Layered clothing](../../resources/beyond-the-dark/layered-clothing.md) shows how we used this new system to leverage the new cage deformer feature to fit suits to these characters.
- [Sound design](../../resources/beyond-the-dark/sound-design.md) details how we set up Roblox's SoundService and place sounds for maximum immersion.
- [User interface](../../resources/beyond-the-dark/user-interface.md) describes how to add a literal layer to the experience and digs into how we built the parallax effect on the map as well as the local holo-screens.

View file

@ -0,0 +1,446 @@
---
title: Layered clothing example
description: Explains the concepts for applying layered clothing in Beyond The Dark.
next: /resources/beyond-the-dark/sound-design
prev: /resources/beyond-the-dark/custom-characters
---
Layered clothing lets you create 3D clothes and accessories that nearly any avatar can wear with any combination of other layered clothes. Layered clothing naturally stretches over an avatar body and other layered clothing items without clipping or breaking.
We recommend building a base body character upon which you can fit the layered clothing you're creating. We'll go over how to do this in an external DCC application and combine it all in Studio to create the final character from the space station.
<Alert severity="info">
For additional layered clothing resources, such as guides, example projects, and reference models, see [3D accessories](../../art/accessories/index.md).
</Alert>
## Build the custom character
We wanted the users in our experience to embody someone sent to the station to investigate some disturbing events. We weren't sure early on what our actual character would be or if there would be multiple types of characters, but we wanted them to wear space suits. Because of this, we decided to make the clothing a separate component which would allow flexibility in our design.
We ultimately settled on a single character design that we named "The Visitor," but layered clothing allowed us to create a main character while also releasing the clothing to the Marketplace as a modular component. As a creator, you'll be able to do the same soon, so follow along to see how we did this.
<img
alt="Custom Character Example"
src="../../assets/resources/beyond-the-dark/layered-clothing/Custom-Character-Example.png"
width="80%" />
### Make the base body
The base body is essentially the lowest layer of the layered clothing system. This can be anything from our character's body in underwear, to a fishman or even a blocky avatar.
Layered Clothing Checklist:
- The body geo, cage parts, and skeleton must conform to the R15 schema.
- The body cage must use the cage supplied by Roblox.
- The cage should be in the same number of parts as the body geo, with the suffix \_OuterCage.
- LeftUpperArm_Geo (mesh)
- LeftUpperArm_OuterCage (cage)
- The poly budget for each part cannot exceed 10,000 triangles.
- Character size cannot exceed 2000×2000×2000 units.
### Make the model
Layered Clothing lets clothes fit on almost any kind of body, whether it be a fishman, a rock golem, or a human space explorer. The only requirements are that they be bipedal humanoids.
You should model the character in a neutral and natural pose. This allows a more full range of motion when animating, and also gives you implied directionality to movement. Implied directionality means that when you're building your character's skeleton, the joints have a small bend in them to know which way they should naturally bend. The following examples show a very small bend in the arms and legs to allow for this. We also posed the hands in a natural posture that could still hold onto something if we wanted items or gear in our experience.
<img
alt="Unique Character Examples"
src="../../assets/resources/beyond-the-dark/layered-clothing/Unique-Characters.png"
width="80%" />
The silhouette, or the shape of your character, should also be distinct and discernable to another player at a distance, which is important for multiplayer experiences. This cuts down confusion and helps users know what is safe or dangerous when a lot of things are going on.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Silhouette-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Silhouette-2.png" />
</GridContainer>
An important note about the base character: Since this is a player avatar, and we wanted to eventually release the body and clothing on the Marketplace, it still needs to follow the R15 schema. This includes the body being separated into 15 parts.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Character-Model.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Character-Model-Exploded.png" />
</GridContainer>
To make sure the parts still appear contiguous, it's recommended you cap your mesh components and then use your application to "average" the vertex normals between body part "seams." This will treat the two vertices on either side of the body part as one, with regards to the surface normals.
<img
alt="Influences Between Seams Example 1"
src="../../assets/resources/beyond-the-dark/layered-clothing/Seams-Example-1.png"
width="80%" />
<img
alt="Influences Between Seams Example 2"
src="../../assets/resources/beyond-the-dark/layered-clothing/Seams-Example-2.png"
width="80%" />
### Texture
Texturing your characters lets you bring them to life based on how you want them to look. For the space station, we wanted a character that was a similar level of realism as its surroundings and settled on the following character:
<img
alt="Texturing Example"
src="../../assets/resources/beyond-the-dark/layered-clothing/Texturing-Showcase.png"
width="80%" />
Now we'll go over how to get textures into Studio and make sure the character looks great and fits into the experience.
1. Before texturing, we did a range of motion tests by moving the character joints and posing it to make sure the model deforms well. It's not always necessary to do a range of motion tests before texturing, but it'll help you catch surprises in your model before you put hours into the textures. After that, we separated the rig from the mesh (unbound it) to focus on texturing. It's not advisable to do texture work, or anything beyond animating, once a character is bound to its rig.
<img
alt="Motion Test Example"
src="../../assets/resources/beyond-the-dark/layered-clothing/Motion-Test.png"
width="80%" />
2. To allow for skin-tone controls for the Marketplace, we made areas like the shirt, shorts, gloves, and boots fully opaque. However, for the areas with exposed skin, we used partial transparency to allow for details to be added to the skin tone, but not completely override it. This allowed us to enhance features of the anatomy beyond what Studio's current lighting can do.
<img
alt="Texturing and UV Diagram"
src="../../assets/resources/beyond-the-dark/layered-clothing/UV-Diagram.png"
width="80%" />
The following images show color (albedo), metalness, roughness, and normal maps.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/UV-Maps-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/UV-Maps-2.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/UV-Maps-3.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/UV-Maps-4.png" />
</GridContainer>
And the following images show skin tone transparency in action in Studio, and show the same color map with transparency and a skin tone color applied.
<GridContainer numColumns="3">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skin-Tone-Maps-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skin-Tone-Maps-2.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skin-Tone-Maps-3.png" />
</GridContainer>
<img
alt="Various Texture Comparisons"
src="../../assets/resources/beyond-the-dark/layered-clothing/Texture-Comparisons.png"
width="75%" />
### Cage
Once we established the base body, we needed to cage it so that layered clothing could fit on it. Cages are the way that the clothing and body interact. Both the body and the clothing have the same cage, deformed by the user to define the clothing's fit. The body cage tells the clothing the shape of the character without all the fine details of the mesh itself. The clothing's inner cage defines how tight the clothing's fit is.
The first pass of fitting the cage, for now, needs to be done within an external DCC application. You can use the cage supplied by Roblox to start.
The cage for the body is largely the same as that of the clothing, except the final result is a cage that has 15 parts (each corresponding to a body part mesh). Below is the "editable" cage which is a single mesh, the cage parts which are deformed by editing the "editable" cage, and the mesh itself.
<img
alt="Individual Body Part Cages"
src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-1.png"
width="80%" />
When you first start with the cage, it's very likely it won't match your character, so you'll need to adjust the "editable" cage, or the parts directly, to fit to the outer bounds of the body mesh. As with clothing, the outer cage should always fit outside the body mesh, so that clothing knows the shape of the body itself. It uses a "twin" cage in the clothing to essentially snap clothing to this outer cage (we'll go over this in more detail below).
<img
alt="Cage Adjustment Example"
src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-2.png"
width="80%" />
We decided that we wanted the outer cage to cover the skin, but not the gloves and boots. Other than those areas, you'll see that the cage fits as best as it can outside the body, including the face cage matching the distance and shape of facial features.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-3.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-4.png" />
</GridContainer>
<img
alt="Outer Cage Example 3"
src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-5.png"
width="80%" />
<img
alt="Outer Cage Example 4"
src="../../assets/resources/beyond-the-dark/layered-clothing/Body-Cage-Example-6.png"
width="80%" />
### Skin
Skinning the character, similar to Custom Characters, is what gives the organic bending and twisting of your character when moving through the environment. We found the following guidelines useful, so that the character mesh imports correctly into Studio:
- Individual joints and bones must have unique names.
- Meshes should have no transforms prior to skinning/binding; in other words, transforms should be 0 and scales should be 1.
- Mesh normals should face outward (the model shouldn't look inside-out).
- Skeletons shouldn't have any scale factor; all joints should be [1, 1, 1].
- Skinning this character was a bit more difficult given the limitations of joint count, but here are some notes from the work we did:
- When skinning a human with clear anatomy, it's best to paint your weights so that bending looks natural, for example the shoulder muscle stretching or compressing when you move the arm up and down.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Influence-Example-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Influence-Example-2.png" />
</GridContainer>
- Another common issue with skinning humans is that skinning defaults will often times be "too smooth," meaning areas like the elbows and knees will have influences distributed too much. It's best to paint them tighter so your legs/arms don't compress when they bend.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Influence-Example-3.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Influence-Example-4.png" />
</GridContainer>
- Lastly, because your model is split into 15 parts, you'll need to make sure seams between the parts are 1:1 in terms of weighting. Most applications will allow you to copy influences from one vertex to another so they have the exact same weighting.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Shoulder-Seams-Example-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Shoulder-Seams-Example-2.png" />
</GridContainer>
### Gloves and boots
As you'll notice from the base body, we decided to include gloves and boots. You might want to do this temporarily as well, for the following reasons:
1. We wanted gloves to be part of the Layered Clothing suit, but gloves aren't currently supported in the Layered Clothing feature, so we replaced the hands of our initial model with one that had gloves.
2. We wanted the metal ring of the shoe to be rigid and not deform to fit the foot and leg, so we replaced the feet of our initial model with one that had boots.
<img
alt="Comparison After Replacing Feet and Hands"
src="../../assets/resources/beyond-the-dark/layered-clothing/Replacing-Hands-And-Feet.png"
width="80%" />
3. To make sure Layered Clothing didn't try to fit around the boots or gloves, we intentionally kept the cages inside them. This enables the top and bottom of the space suit to deform to the hands and feet.
<img
alt="Caging with Gloves"
src="../../assets/resources/beyond-the-dark/layered-clothing/Caging-With-Gloves.png"
width="80%" />
## Build the suit
Early on in the process, we decided that the suit should fit onto multiple characters, and that we wanted to release the assets to the Marketplace so others could enjoy the clothing. In addition to Layered Clothing, our character needed "hardpoint" items like a helmet and backpack as finishing touches. The following image shows how the space suit looks on a variety of characters.
<img
alt="Layered Clothing Suit Comparison"
src="../../assets/resources/beyond-the-dark/layered-clothing/Layerable-Suit-Comparison.png"
width="80%" />
We found the following guidelines useful when building Layered Clothing:
- The exported `.fbx` file from your DCC application must contain the **clothing mesh**, **inner cage**, **outer cage**, and **skeleton**.
- The way you name your clothes is arbitrary, but both cages need to have this naming format:
- [clothing name]**\_InnerCage**
- [clothing name]**\_OuterCage**
- You must skin clothing to the R15 skeleton with a maximum of **4** influences.
- Your R15 skeleton must follow this naming schema.
With those guidelines in mind, here are the steps we followed to build the space suit:
1. Use the Roblox Layered Clothing cage template, a base frame that you can use to fit clothing to. Depending on the distance of the clothing to the frame, you can have clothing that is tight or loose fitting.
2. Model the clothing to the frame and sculpt a higher-poly model in an external DCC application.
3. Rebuild the lower-poly, game-ready version of the higher-poly model. If you're interested in more information about this process, there are many tutorials online for high-poly to game-ready models. Note that clothing does not need to be split up into parts corresponding to the body parts they're covering; it can be a single mesh.
<img alt="Clothing Design Example 1"
src="../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Design-Example-1.png"
width="80%" />
<img alt="Clothing Design Example 2"
src="../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Design-Example-2.png"
width="80%" />
4. Test the movement and fitness on other characters prior to finalizing any textures.
5. Define the "outer cage" or the outer bounds of the clothing item. This tells any additional layers how tightly to fit to the astronaut suit top.
<Alert severity="warning">
It's very important for clothing outer cages to follow the clothing mesh closely, but always be outside the clothing. This allows Studio to also employ another powerful feature, Hidden Surface Removal. This feature removes geometry of a layer of clothing or even the body, if it's covered by another layer. This is immensely useful, because you won't have to worry about any meshes interpenetrating when the character runs around.
</Alert>
<img alt="Layered Clothing Outer Cage Example"
src="../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Outer-Cage.png"
width="80%" />
6. After caging, it may help to temporarily skin the clothing to the skeleton in the template file, just to do a range of motion test. By posing the rig in different positions, you can confirm it deforms well before finalizing texturing. If you do this, be sure to unbind before moving to texturing!
<img alt="Layered Clothing Rig Binding"
src="../../assets/resources/beyond-the-dark/layered-clothing/Clothing-Posing.png"
width="80%" />
### Texture
After we skinned the clothing, and it moved the way we intended, we moved to texturing.
1. Because we wanted to aim for realism, we used surface appearance for the beautiful PBR materials. We brought this into a texturing application to make the albedo, metalness, roughness, and normal maps. This allowed the outfit to have parts that looked metallic and others to look more like rough canvas.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Texturing-Example-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Texturing-Example-2.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Texturing-Example-3.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Texturing-Example-4.png" />
</GridContainer>
<img
alt="Surface Appearance Lighting Test 1"
src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Lighting-Test.gif"
width="80%" />
2. Before going much further, we brought the model and textures into Studio to test the materials with the proper lighting in the space station. This led to some adjustments to the metalness of the arm and torso bands.
<img
alt="Surface Appearance Lighting Test 2"
src="../../assets/resources/beyond-the-dark/layered-clothing/Suit-Lighting-Test-2.png"
width="80%" />
When texturing your clothing, keep the following points in mind:
- For surface appearance, make sure to include color (albedo), metalness, roughness, and normal maps.
- Without metalness, the surface appearance defaults to 0 metalness, or a plastic surface.
- Without roughness, the surface appearance defaults to 0 roughness, or a smooth surface.
### Skin
Skinning clothing is required to have it bend properly with the base body. By skinning it, you're also telling the animations how the clothing should bend, including which parts are rigid or move with the character.
<img
alt="Skinned Model Animation"
src="../../assets/resources/beyond-the-dark/layered-clothing/Skinning-Movement.gif"
width="80%" />
Luckily, unlike the base body, you don't need to split up your clothing, so the suit top can be a single mesh and skinned as such. To save yourself time, most applications will allow you to only skin your mesh to certain joints. The joints we wanted to affect the suit top were only upper/lower torso, upper/lower arms, and the head. Not binding to the whole body saved time removing unnecessary weighting.
<img
alt="Maya Character Vertex Influences Example 1"
src="../../assets/resources/beyond-the-dark/layered-clothing/Vertex-Influence-Example-1.png"
width="80%" />
Much like the body, though, you'll want to skin this so it bends naturally given its material. This meant for the middle plastic section, we had a solid weighting to the upper torso.
Since the gloves were going to be rigid, and the suit top stopped well above the wrist, we didn't feel it was necessary for it to bend with the hands.
<img
alt="Maya Character Vertex Influences Example 2"
src="../../assets/resources/beyond-the-dark/layered-clothing/Vertex-Influence-Example-2.png"
width="80%" />
Once skinning on the suit top was done, we simply repeated the same procedure on the pants to get them ready for Studio and bringing into the engine!
### Import to Studio
When the clothing looked good in our DCC application and in Studio, we were ready to import it into Studio and use the Accessory Tool, an upcoming release which will help you refine the clothing's cages to get the best fit possible. The tool can also export clothing as an accessory, which lets you add your clothing to a character.
In Studio, we named our Layered Clothing items with an LC\_ prefix, so we could distinguish between them and legacy style accessories (the helmet and backpack parts of the suit).
<img
alt="Accessories in the Studio Explorer"
src="../../assets/resources/beyond-the-dark/layered-clothing/Accessories-In-Explorer.png"
width="320" />
## Build the helmet
The helmet is an important element of the suit. We wanted users to see the character's face and also capture the sheen of the helmet's glass surface.
<img
alt="Helmet Glass Surface Render"
src="../../assets/resources/beyond-the-dark/layered-clothing/Glass-Surface-Example.png"
width="80%" />
Accessories where we don't want deformation to fit the head, like this helmet, are best built as "hardpoint" accessories, which are the types of accessories that you currently find in the Marketplace. The main difference with the helmet and what you find in the Marketplace is that it uses `Class.SurfaceAppearance`, which creates convincing plastic and metal looks and gives the helmet visor some transparency.
### Make the model
To build a helmet model:
1. We established a good silhouette when we placed the helmet on the character's head.
<img
alt="Helmet Silhouette"
src="../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Silhouette.png"
width="80%" />
2. We reused the trim maps from the space station to keep the amount of textures to a minimum. We modeled the helmet with enough separate sections so we could place the UVs to best utilize the texture maps.
<img
alt="Helmet Texture Maps"
src="../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Texture-Map.png"
width="80%" />
3. We then used the same trim maps we used in many areas of the station.
<GridContainer numColumns="2">
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Trim-Map-Texture-1.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Trim-Map-Texture-2.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Trim-Map-Texture-3.png" />
<img src="../../assets/resources/beyond-the-dark/layered-clothing/Trim-Map-Texture-4.png" />
</GridContainer>
### Import to Studio
When we had the helmet roughly fitted and placed in our DCC application, we could then import it into Studio with all the effects we wanted.
1. We initially imported the helmet using the **Asset Manager**, but because it's a single mesh, you can also drop a `Class.MeshPart` into your place and specify the MeshId using the **Properties** window.
<img
alt="Importing Helmet Mesh"
src="../../assets/resources/beyond-the-dark/layered-clothing/Importing-Helmet-Mesh.jpeg"
width="80%" />
2. We then added the texture maps via a `Class.SurfaceAppearance` object on the mesh part. This process took some tuning to get the ideal alpha on the color (albedo) map, so you could see through the helmet without it being fully transparent.
<img
alt="Texturing Helmet Mesh"
src="../../assets/resources/beyond-the-dark/layered-clothing/Adding-Surface-Appearance.jpeg"
width="80%" />
3. Because we wanted some suits to be clean and some to be dirty without having to use multiple textures, we added decals to the same object to create this effect. Decals work by planar projecting onto each surface.
<img
alt="Adding Additional Textures Helmet Mesh"
src="../../assets/resources/beyond-the-dark/layered-clothing/Adding-Surface-Appearance-2.jpeg"
width="80%" />
4. We wanted a light beam and light to indicate which way the user was facing in dark rooms and to illuminate the areas that they're walking through. To do this, we used attachments, because you can add an arbitrary amount of them, and because they're not parts, they're valid in accessories. You can use attachments for particle effects, beams, or lights as in the following example.
<img
alt="Adding Lighting to Helmet Mesh"
src="../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Attachments.jpeg"
width="80%" />
### Introduce the accessory tool
<Alert severity="info"> See [Accessory Fitting Tool](../../art/accessories/accessory-fitting-tool.md) for more information on converting a mesh or model to an `Class.Accessory`. </Alert>
With so many components being built in parallel, it can be a little hard to know if your assets fit exactly and look the way you want. However, with the upcoming Accessory Tool, fitting and refining clothing is much easier. We used this tool to set up the character's top and bottom, as well as the hardpoint helmet and backpack accessories.
<img
alt="Character and Suit Overview"
src="../../assets/resources/beyond-the-dark/layered-clothing/Character-And-Suit-Overview.png"
width="80%" />
Here's a summary of using the Accessory Tool for layered clothing:
1. We first fitted the suit's top to the character. Luckily, the fit was great and our inner cage conformed to the body well. We also tested the outer cage with other clothing items. When we were happy with the fit, we used the tool to generate the appropriate accessory for the Marketplace.
<img
alt="Using Accessory Fitting Tool Mannequin"
src="../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Tool-Mannequin.png"
width="80%" />
2. With the suit's bottom, we wanted to adjust the fit within Studio because they were initially too bulky. Using the point editor, we nudged things to get them into position. Like the suit top, we made it into an accessory.
<img
alt="Adjusting Layered Clothing with Accessory Fitting Tool"
src="../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments.png" width="80%" />
3. For the helmet and backpack, we adjusted these by just scaling, rotating, and moving them where we wanted, because they don't have cages.
<img
alt="Adjusting Layered Clothing Helmet with Accessory Fitting Tool"
src="../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments-2.png"
width="80%" />
4. Seeing how the helmet fits without the rest of the suit is tricky, so we used the Accessory Tool to visualize the top and bottom of the suit. This allowed us to ensure the fit would align with the suit's top.
<img
alt="Adjusting Layered Clothing Helmet with Accessory Fitting Tool"
src="../../assets/resources/beyond-the-dark/layered-clothing/Accessory-Fitting-Tool-Adjustments-3.png"
width="80%" />
5. With the helmet, backpack, top, and bottom complete, we were ready to see how this character looked in our space station. We copied and pasted the accessories onto the character and were able to see how they fit. We then placed the character in our place file and confirmed all the materials, lighting, and other effects were exactly the way we wanted.
<img
alt="Layered Clothing Accessories Final Render"
src="../../assets/resources/beyond-the-dark/layered-clothing/Helmet-Equipped-Final.png"
width="80%" />

View file

@ -0,0 +1,81 @@
---
title: Sound design
next: /resources/beyond-the-dark/user-interface
prev: /resources/beyond-the-dark/layered-clothing
description: Explains the design concepts for sound in Beyond The Dark.
---
<img
alt="Sound Design Banner"
src="../../assets/resources/beyond-the-dark/sound-design/Sound-Banner.jpeg"
width="80%" />
The approach we used to fill the space station with audio was to implement a 2D omnipresent soundbed as our base sound and use 3D sounds attached to objects (spot ambiences) to pull the user's ear in different directions as they explore the ship. We focused a lot on immersion and making the ship feel as real as possible when creating and placing these sounds. The result is a vibrant environment that's full of sonic diversity with pockets of fun audio discovery.
The DSR-14 spaceship contains the following audio that were sent through a custom mix hierarchy that lowers and raises the volume level of each sound group dynamically:
- One 2D soundbed
- Two dialogue tracks
- Three custom music tracks
- 10 scripted audio events
- 50 3D sounds
## Design sound assets and effects
The space station's fish tank emits some of our favorite sounds in the experience. It's a perfect encapsulation of the design philosophy that we employed, using spot ambiences to make the space feel dynamic and fresh every time you enter or exit a distinct area. Other places we did this included neon light buzzes, fans, and the black hole rumble.
As seen in the following screenshot, the looping fish tank sound is a child of a part inside the tank to give it a location in the scene to play from.
<img
alt="Sound Design In-Studio Location Example"
src="../../assets/resources/beyond-the-dark/sound-design/In-Studio-Sound.jpeg"
width="80%" />
The fish tank sound uses two main layers: a saw-wave synthesizer loop and a recording of a river in the Catskill Mountains in New York. This created the feeling of a high-tech motor pushing water through the tank. We applied an `Class.EqualizerSoundEffect` to the river sound to eliminate high frequencies and give it a more "underwater" feeling.
<img
alt="Sound Design In-Studio Location Example"
src="../../assets/resources/beyond-the-dark/sound-design/In-Explorer-Sound.png"
width="320" />
We applied a `Class.ChorusSoundEffect` on the synthesizer loop to make it sound more other-worldly. This chorus adds a warbly de-tuned effect to whatever content you pass through it. It is a great way to make something sound strange or unnatural, and it's also a common effect used in music. Here, we've picked a rate of 16 cycles per second, and a fairly mild chorus depth of 0.21. With these settings, some of the original character of the sound still comes through.
<img
alt="Sound Design In-Studio Location Example"
src="../../assets/resources/beyond-the-dark/sound-design/Sound-Properties.png"
width="320" />
When we layer these sounds together, it fits the object and space very well. However, you might be wondering why we didn't export a looping sound that sounds like this from the beginning and implement it as one object. There are two main benefits to layering the sound like this:
- We get more uses from the same content by reusing the river sound in the rainforest room.
- We get real-time randomness that keeps the sound varied and interesting over long stretches of time. Two layered sound loops become as long as their least common multiple. In other words, a six second loop and a five second loop remains unique for 30 seconds, increasing audio variety for free.
Other sound assets were created for multiple scripted sequences on the ship, for example the black hole triggering the anti-gravity effect. These assets were largely made from recordings of a toaster oven door, pitched down and run through various reverb and EQ effects. Pitching this source material down implies the large size of the ship, and the reverb does a great job of continuing to reinforce that effect, by making the whole thing feel like a cave. Some synthesized laser elements were also generated and added for additional accent and impact on the events.
## The mix
The mix uses a nested bus structure with sound groups, and two sidechain compressors. The main goal of the mix was to create clarity and listener interest.
<img
alt="Sound Design In-Studio Location Example"
src="../../assets/resources/beyond-the-dark/sound-design/Sound-Structure.png"
width="320" />
When creating a mix for an environment with as much excitement and ambient activity as this ship, it's important not to let things get out of hand sonically. This means creating priority through sidechains. Let's take a look at one of the sidechain compressors on the generic ambience `Class.SoundGroup`.
<img
alt="Sound Design In-Studio Location Example"
src="../../assets/resources/beyond-the-dark/sound-design/Sound-Structure-Properties.png"
width="320" />
A sidechain compressor allows one audio source to duck the volume of another based on its current volume. This is an effect commonly used in podcasts, music, games, and other audio media. As you can see in the above image, the Ambience Generic bed is receiving compression from Ambience Local. This means when the player walks up to a local ambience like the waterfall, the generic spaceship soundbed is ducked. This creates a feeling of audio "freshness" around the ship. All of the sounds and interesting areas are creating room for each other to breathe and be the focus of attention.
<Alert severity="info">
The actual settings on this compressor in the image shown, while instructive, are highly specific to these sounds and will be different depending on how loud your source audio and target both are. They cannot be copied and pasted and used with different content to deliver the same effect. Everything is relative to the volume of your content!
</Alert>
A general overview of the three most important settings in the compressor, and how they behave in the sidechain:
1. **Attack / Release** — This is how quickly in seconds that the compressor begins ducking, and how slowly it returns to normal volume after being ducked.
2. **Threshold** — This value determines the minimum volume level at which point the compressor will start to work.
3. **Ratio** — This refers to how intensely the sidechained material will be ducked; a higher ratio indicates more severe ducking.

View file

@ -0,0 +1,229 @@
---
title: User interface
prev: /resources/beyond-the-dark/sound-design
description: Explains the design concepts for user-interface effects in Beyond The Dark.
---
We wanted to add an interactive map UI to let users consume information in the space station that looked and felt like it lived in this world. We decided to build the map inside the 3D space instead of on a screen that overlays the experience. This type of diegetic visualization allows for more immersion with the world as opposed to feeling like it is a completely separate experience.
## Design the map
To design the map:
1. We mocked the UI in an external application and came up with a rough idea of how we wanted it to look.
<img
alt="UI Mock"
src="../../assets/resources/beyond-the-dark/user-interface/UI-Mock.png"
width="80%" />
2. We exported the individual pieces of the map as `.png` and imported them into Studio.
<img
alt="UI Elements Exported"
src="../../assets/resources/beyond-the-dark/user-interface/UI-Export.png"
width="80%" />
## Build the map
Building the map inside Studio involved using `Class.Part|Parts` and `Class.SurfaceGui|SurfaceGuis`.
1. For non-interactive elements, all we needed to do is add a `Class.SurfaceGui` object to the part.
2. For interactive elements, the `Class.SurfaceGui` also needs to be inside the `Class.StarterGui` container, with the `Adornee` property linked to the appropriate part in the 3D workspace. Doing so allows you to add button events.
<video
controls
src="../../assets/resources/beyond-the-dark/user-interface/StarterGui-Adornee.mp4"
width="320"></video>
3. To achieve a parallax effect, we used three separate `Class.ScreenGui` instances assigned to three unique `Parts` with different **X** values.
<img
alt="Parallax Example"
src="../../assets/resources/beyond-the-dark/user-interface/Parallax-Example.png"
width="80%" />
4. We then added a glow effect with the `Class.SurfaceGui.LightInfluence` property. If you set the property value to anything less than 1, it enables the `Class.SurfaceGui.Brightness` property. By adjusting the brightness, you can increase the glow emitting from the image.
<video
controls
src="../../assets/resources/beyond-the-dark/user-interface/Light-Properties.mp4"
width="75%"></video>
5. To let users toggle the display of the map, we used a `Class.ProximityPrompt` that we attached to a 3D model. This is an easy way to allow user interaction with world elements.
<img
alt="Proximity Prompt in Explorer"
src="../../assets/resources/beyond-the-dark/user-interface/Proximity-Prompt.png"
width="320" />
6. Finally, using a UITweenModule ModuleScript inside ReplicatedStorage, we animated hiding and showing the UI with TweenService and a bit of logic for determining the state. By tracking what the user clicked, we could hide and show elements by tweening various properties like alpha, position, and size.
<img
alt="PUI Tween Module in Explorer"
src="../../assets/resources/beyond-the-dark/user-interface/UITweenModule.png"
width="320" />
```lua title="UITweenModule ModuleScript"
local TweenService = game:GetService("TweenService")
local UITween = {}
-- for fading images
function UITween.fadePart(object, amount, time, delay)
local tweenAlpha = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quad, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay --Delay time
)
local tween = TweenService:Create(object, tweenAlpha, {Transparency = amount})
tween:Play()
end
function UITween.fade(object, amount, time, delay)
local tweenAlpha = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quad, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay --Delay time
)
local tween = TweenService:Create(object, tweenAlpha, {ImageTransparency = amount})
tween:Play()
end
-- for fading images
function UITween.fadeBackground(object, amount, time, delay)
local tweenAlpha = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quad, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay --Delay time
)
local tween = TweenService:Create(object, tweenAlpha, {BackgroundTransparency = amount})
tween:Play()
end
-- for fading text
function UITween.fadeText(object, amount, time, delay)
local tweenAlpha = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quad, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay --Delay time
)
local tween1 = TweenService:Create(object, tweenAlpha, {TextTransparency = amount})
tween1:Play()
end
-- for moving text and images
function UITween.move(object, position, time, delay)
task.wait(delay)
object:TweenPosition(position, Enum.EasingDirection.Out, Enum.EasingStyle.Quint, time)
end
-- for changing size
function UITween.size(object, size, time, delay, override, callback)
local tweenSize = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quint, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay, --Delay time
override,
callback
)
local tween = TweenService:Create(object, tweenSize, {Size = size})
tween:Play()
end
function UITween.rotate(object, rotation, time, delay, override, callback)
local tweenSize = TweenInfo.new(
time, --Time
Enum.EasingStyle.Quint, --EasingStyle
Enum.EasingDirection.Out, --EasingDirection
0, --Repeat count
false, --Reverses if true
delay, --Delay time
override,
callback
)
local tween = TweenService:Create(object, tweenSize, {Rotation = rotation})
tween:Play()
end
-- for blurring the game camera
function UITween.blur(object, amount, time)
local tweenInfo = TweenInfo.new(time, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
local tween = TweenService:Create(object, tweenInfo, {Size = amount})
tween:Play()
end
-- for blurring the game camera
function UITween.turnOn(object, amount, time)
local tweenInfo = TweenInfo.new(time, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0)
local tween = TweenService:Create(object, tweenInfo, {Brightness = amount})
tween:Play()
end
return UITween
```
```lua title="Applying UI Tween to Objects"
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Add UITween Module
local UITween = require(ReplicatedStorage.UITweenModule)
-- Find player Guis and UI objects
local playerGui = game:GetService('Players').LocalPlayer:WaitForChild('PlayerGui')
local screenGuiMapUIFrame = playerGui:WaitForChild("ScreenGuiMapUIFrame").SurfaceGui
local mapUIFrameStroke = screenGuiMapUIFrame.FrameStroke
local mapUIFrameFill = screenGuiMapUIFrame.FrameFill
-- Sizes used for tweening
local frameSizeStart = UDim2.new(0, 0, 0, 0)
local frameSizeMid = UDim2.new(1, 0, 0.05, 0)
local frameSizeEnd = UDim2.new(1, 0, 1, 0)
-- Example Tweening
UITween.fade(mapUIFrameStroke, 0, 2, 0)
UITween.size(mapUIFrameStroke, frameSizeMid, 0.4, 0)
UITween.fade(mapUIFrameFill, 0, 2, 0.5)
UITween.size(mapUIFrameFill, frameSizeEnd, 0.4, 0.25)
task.wait(0.25)
UITween.size(mapUIFrameStroke, frameSizeMid, 0.4, 0)
UITween.size(mapUIFrameFill, frameSizeMid, 0.4, 0.25)
task.wait(0.25)
UITween.size(mapUIFrameStroke, frameSizeEnd, 0.4, 0)
UITween.size(mapUIFrameFill, frameSizeEnd, 0.4, 0.25)
```
Here's the final result of the interactive map:
<video
controls
src="../../assets/resources/beyond-the-dark/user-interface/Final-UI.mp4"
width="75%"></video>