Let’s Create a Craft Item that Moves!

Cluster Creators Guide

Cluster Official
6 min readJan 18, 2024

Did you know you can use JavaScript in the Cluster Creator Kit and while in World Craft?

By using scripts, you are able to create moving craft items, even within World Craft!

In this article, we will show you how to create scripted items in World Craft!

Please keep reading this article for instructions on how to use the Cluster Creator Kit!

Uploading a “Scriptable Item” Craft Item

You must attach a “Scriptable Item” to the item you want to make moveable.
Uploading a “Scriptable Item” will allow the item to be edited in World Craft.

Please read this article for information on uploading craft items.

Opening World Craft

Let’s try editing scripts within World Craft!

💡 Script editing in World Craft is only available in the PC version

  • First, open World Craft as you normally would!
  • Once it is open, place the craft item you just uploaded.
    Before you publish them to the store, you will be the only person able to use the craft items you uploaded!

Settings for using the Script Editor

Once you are ready, you can move on to the next step. We will now set up the script editor so that you can use it to edit scripts within World Craft.

  • Open the menu by clicking on the icon of the three horizontal lines in the upper left corner.
  • Select “Settings” from the menu.
  • In the “Settings” section, select “Other” and then turn on the “Console” and “Script Editor” in the “World/Item Creator Features” category.

This completes the setup!
When you look at the craft item that you want to edit, you will see a guide for editable craft items.

💡 No guide will appear when you look at a craft item that doesn’t contain a Scriptable Item on it!

Press F12 to display the script editor in the upper left corner. You can edit the code here!

We Prepared a Modifiable Sample Code for You!

We have prepared a sample code that you can modify and use for your own creations!
If you are curious about the kinds of moving craft items you can make, go ahead and try out this code for yourself! (The code needs a little modification before you can use it, so don’t forget to edit the code first!)
By modifying it, you can also learn how to write your own code.

💡 All code is in Java Script

Doors

△You can tap or click to open and close doors!
△ Make sure the “Scriptable Item” is attached to the parent object.
const door = $.subNode("Model");
const axis = new Vector3(0, 1, 0);

$.onInteract(() => {
let isOpen = $.state.isOpen;
isOpen = !isOpen;
$.state.isOpen = isOpen;
const rot = new Quaternion()
.setFromAxisAngle(axis, isOpen ? -90 : 0);
door.setRotation(rot);
});

△This is a simple sample code for an Interaction. It is processed within onInteract, as there are no animation frames in between the movements.

Moving Floors

△This code is designed to loop left and right movements (you are also able to change the direction to back and forth movements)
△ Make sure the “Scriptable Item” is attached to the parent object.
const tile = $.subNode("Tile");
const period = 3;
const width = 4;

const trapezoidalWave = (t) => {
if (t < 0.25) {
return t * 4;
} else
if (t < 0.5) {
return 1;
} else
if (t < 0.75) {
return 3 - t * 4;
} else {
return 0;
}
};

$.onUpdate(deltaTime => {
let time = $.state.time ?? 0;
time += deltaTime;
$.state.time = time;
const pos = new Vector3(
trapezoidalWave(time % period / period) * width - width / 2,
0,
0);
tile.setPosition(pos);
});

△This sample code creates a loop movement. It updates the position of every frame within onUpdate.

Elevators

△This movement consists of opening and closing the door and moving up and down in a loop.
△ Make sure the “Scriptable Item” is attached to the parent object.
const container = $.subNode("Container");
const doorLeft = $.subNode("Door_L");
const doorRight = $.subNode("Door_R");

const waitTimeSec = 10;
const transitTimeSec = 10;
const period = (waitTimeSec + transitTimeSec) * 2;

const targetHeight = 4;
const offsetHeight = 0.275;
const doorWidth = 0.5;

const easeInOutQuad = (t) => {
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
};

const upwardsCagePosYRatio = (time) => {
if (time <= waitTimeSec) {
return 0;
} else {
return easeInOutQuad((time - waitTimeSec) / transitTimeSec);
}
};

const cagePosYRatio = (time) => {
time = time % period;
if (time < period / 2) {
return upwardsCagePosYRatio(time);
} else {
return 1 - upwardsCagePosYRatio(time - period / 2);
}
};

const doorPosXRatio = (time) => {
time = time % (period / 2);
if (time <= waitTimeSec) {
return 1;
} else {
return 0;
}
};

$.onUpdate(deltaTime => {
let time = $.state.time ?? 0;
time += deltaTime;
$.state.time = time;
container.setPosition(
new Vector3(
0,
cagePosYRatio(time) * targetHeight + offsetHeight,
0));
doorLeft.setPosition(
new Vector3(doorPosXRatio(time) * doorWidth, 0, 0));
doorRight.setPosition(
new Vector3(doorPosXRatio(time) * -doorWidth, 0, 0));
});

△ This is a sample code for a slightly more advanced loop movement, which can be enhanced by applying “easing” to make it look more like a loop.

Collapsible Walls

△You can tap or click to make the wall collapse!
△ The “Scriptable Item” should be attached to the parent object.
const container = $.subNode("Container");
const minAngle = 0.0;
const maxAngle = 90.0;
const openTimeSec = 0.8;
const closeTimeSec = 2;

const lerp = (a, b, t) => {
if (b == a) return a;
t = Math.min(Math.max(t, 0), 1);
return a + t * (b - a);
};

const easeInQuart = (t) => {
return t * t * t * t;
};

const easeOutBounce = (t) => {
const n1 = 7.5625;
const d1 = 2.75;

if (t < 1 / d1) {
return n1 * t * t;
} else if (t < 2 / d1) {
return n1 * (t -= 1.5 / d1) * t + 0.75;
} else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else {
return n1 * (t -= 2.625 / d1) * t + 0.984375;
}
};

const easeInBounce = (t) => {
t = Math.min(t, 1);
return 1 - easeOutBounce(1 - t);
};

$.onUpdate(deltaTime => {
let time = $.state.time ?? 0;
time += deltaTime;
$.state.time = time;

let angle = minAngle;
if ($.state.open) {
angle = lerp(minAngle, maxAngle, easeInQuart(time / openTimeSec));
if (time > 5) {
$.state.time = 0;
$.state.open = false;
}
} else {
angle = lerp(maxAngle, minAngle, easeInBounce(time / closeTimeSec));
}

const rot = new Quaternion()
.setFromEulerAngles(new Vector3(angle, 0, 0));
container.setRotation(rot);
});

$.onInteract(() => {
if (!$.state.open && $.state.time > closeTimeSec) {
$.state.time = 0;
$.state.open = true;
}
});

△This image is a further development of the door, which also uses easing to give the movement a more gradual and rapid motion.

--

--

Cluster Official

Cluster is a social virtual reality platform that brings people together to accelerate human creativity. Check out our website: https://cluster.mu/en/welcome