Skip to content
Swifter edited this page Feb 9, 2025 · 21 revisions

Prerequisites

Property Translations

What is this?

Raw JSON ReMapper
d duration
h height
w width

Creating

Walls can be created like any other beatmap object:

rm.wall(map, {
    width: 2,
    height: 2,
    x: 0,
    y: 1,
    chromaColor: [0,0,0]
})

A difficulty has a walls array instead of an obstacles array. If more obstacles end up being a thing, they will have separate arrays too.

World To Wall

Getting a transformation from world space to local space for walls can be quite hairy. It's a complicated mixture of world offsets and local offsets. It's also treated differently depending on whether size or animated scale (scale defined in the animation object) is being used.

Size will scale the object from it's bottom-left-front corner, like so:

static scale (copy)

Animated scale will scale the object from it's bottom-front edge at the middle, like so:

animated scale (copy)

The goal of the rm.worldToWall function is to provide the necessary transformations required to align a wall to some transform. It assumes the center of the wall is the desired origin of the transform.

For simplicity's sake, either size or animated scale must be default. Depending on which one is being used, the scaleIsAnimated property will be set in the worldToWall function.

Output Interpretation

Here I'll show you can take some desired transform transform, and apply it to a wall.

If we're using size, this is how the data from worldToWall is to be interpreted:

const transform: rm.Transform = {
    position: [0, 0, 0],
    rotation: [0, 90, 0],
    scale: [2, 2, 2]
}
const wtw = rm.worldToWall(transform, false)

rm.wall(map, {
    life: 69420,
    lifeStart: 0,
    size: wtw.scale,
    coordinates: [0, 0], // removes any lane offsets to the wall
    animation: {
        definitePosition: wtw.position,
        localRotation: transform.rotation,
    },
})

If we're using an animated scale instead, we interpret the result like so:

const transform: rm.Transform = {
    position: [0, 0, 0],
    rotation: [0, 90, 0],
    scale: [2, 2, 2],
}
const wtw = rm.worldToWall(transform, true)

rm.wall(map, {
    life: 69420,
    lifeStart: 0,
    size: [1, 1, 1], // size MUST be 1
    coordinates: [0, 0],
    animation: {
        scale: wtw.scale, // scale is instead used in here
        definitePosition: wtw.position,
        localRotation: transform.rotation,
    },
})

Set World Transform

If you don't want to interact with worldToWall and just want to set a wall's world transform, you can use the Wall.setWorldTransform function. It can handle animated transforms as well, which automatically use the bake system.

const w = rm.wall(map, {
    life: 10,
    lifeStart: 0,
})

// Non animated, uses size
w.setWorldTransform({
    position: [0, 0, 10],
    rotation: [0, 20, 0],
    scale: [1, 3, 2],
})

// Animated, bakes
w.setWorldTransform(
    {
        position: [
            [0, 0, 0, 0],
            [0, 10, 200, 1, 'easeInOutExpo'],
        ],
        rotation: [
            [0, 90, 0, 0],
            [0, 0, 90, 1, 'easeInOutExpo'],
        ],
        scale: [
            [0, 90, 0, 0],
            [0, 0, 20, 1, 'easeInOutExpo'],
        ],
    },
)

Internally this uses the rm.setWallWorldTransform function which references the rm.bakeWallWorldTransform function if it needs to bake.

Model To Wall

The rm.modelToWall function is designed to represent model objects as walls. It takes in the relevant difficulty and an array of model objects or a string to the path of a .rmmodel file as an input for the model. You are also required to define the start and end of the walls' lifetime.

The model objects are interpreted as unit cubes with the anchor point at their center. The color property is interpreted as Wall.chromaColor, and the group property is ignored.

rm.modelToWall(map, 'my_model', 0, 200)

The function returns a promise for an array of walls which have been used to construct the model.

const modelWalls = await rm.modelToWall(map, 'dormant/env', 0, 200)

// Example: make all resulting walls green
modelWalls.forEach((wall) => {
    wall.chromaColor = [0, 1, 0]
})

The optional distribution parameter controls the total spacing in beats that the walls are spawned. Walls are slightly shifted backwards from 0 to distribution beats in order to combat lag spikes from spawning too many walls at once. By default distribution is 0.3 beats.

Their animations account for this shift (unless they are a string pointing to a point definition in the difficulty) in order to make the distribution seamless. AssignPathAnimation events will also not be able to account for the differing lifetimes of the walls.

rm.modelToWall(map, 'dormant/env', 0, 200, 3) // spread spawning of walls over 3 beats

Clone this wiki locally