Models
A model is many displays that move as one. Use a model when one block or item is not enough: a lamp, a windmill, a pet with a body and a head.
Parts can be blocks, items or texts. Every part has a name. You use the name later to move or change that part.
If you draw your model in an editor, read Importing instead. This page is for building models in code.
Your first model#
MotionDisplay lamp = motion.model(location)
.block("post", Material.DARK_OAK_FENCE.createBlockData(), Pose.IDENTITY.withOffset(0f, 1f, 0f).withScale(1f, 2f, 1f))
.item("light", ItemStack.of(Material.LANTERN), Pose.IDENTITY.withOffset(0f, 1.3f, 0f), "post")
.text("sign", Component.text("Shop"), Pose.IDENTITY.withOffset(0f, 0.5f, 0.2f).withScale(0.5f), "post")
.spawn()
.play(Animations.popIn(20));
How to read it:
motion.model(location)starts a model. The location is the point where it stands..block("post", ...)adds a block part namedpost. Its offset is(0, 1, 0)and its scale is(1, 2, 1). So it is a post that is one block wide and two blocks tall, and it stands on the location..item("light", ...)adds a lantern. The last text"post"is the parent. The lantern moves with the post..text("sign", ...)adds a text part, also under the post..spawn()makes the model. It returns aMotionDisplay..play(...)plays an animation on the whole model.
For a block part, the offset is where its center goes and the scale is its size in blocks. A block with scale (1, 2, 1) is a post two blocks tall. To make it stand on the floor, put its center one block up.
Rules#
- A parent must be added before its children.
- Names must be different. Two parts with the same name give an error.
- Offset and rotation of a part pass to its children.
- Scale of a part is its own size. Children do not get it.
- Animations pass everything to the children, scale too.
- Blocks turn and scale around their center. Items around their model center. Text grows up from its pose.
- The model faces the yaw and pitch of the spawn location.
- At most 256 parts.
Parents#
A parent is a part that other parts follow. When the parent moves or turns, the children go with it. This is how you make arms, doors and wheels.
windmill.play(Animations.popIn(25)); // the whole model
windmill.play("axle", Animations.turn(0f, 0f, -360f, 120).loop()); // the axle and everything on it
Tip: when many parts turn around one point, put them under one parent and turn the parent. This is easier than turning every part.
Groups#
A group is an invisible pivot. Nothing is shown for it. It only holds children, and you can turn or move it.
Use a group when the point you want to turn around is not a block. A door turns around its hinge. A lid turns around its back edge.
motion.model(location)
.group("hinge", Pose.IDENTITY.withOffset(0f, 0.5f, -0.4f))
.block("lid", Material.DARK_OAK_PLANKS.createBlockData(), Pose.IDENTITY.withOffset(0f, 0f, 0.4f).withScale(1f, 0.2f, 0.8f), "hinge")
.spawn()
.play("hinge", Animations.turn(-70f, 0f, 0f, 10)); // the lid opens around the hinge
The hinge is the pivot. The lid sits 0.4 blocks away from the hinge. When the hinge turns, the lid swings up like a real lid.
Matrix parts#
If you already have a 4x4 matrix (from /summon, another tool, or your own math), give it row by row:
float[] matrix = {0.5f, 0, 0, -0.25f, 0, 0.5f, 0, 0, 0, 0, 0.5f, -0.25f, 0, 0, 0, 1};
model.block("cube", Material.STONE.createBlockData(), matrix, null);
Matrix parts work exactly like vanilla: blocks turn around their corner and the whole matrix, scale too, passes to the children.
There are matrix versions of block, item, text and group. The importers use them.
Looks per part#
These calls change how one part looks. Leave the part name out to change every part.
lamp.glow("light", Color.ORANGE);
lamp.brightness("sign", new Display.Brightness(15, 15));
lamp.billboard("sign", Display.Billboard.CENTER);
lamp.text("sign", Component.text("Open"));
lamp.background("sign", Color.fromARGB(0, 0, 0, 0)); // no dark box behind the text
glowdraws a colored outline. Usenullto turn it off. Text parts do not glow.brightnesssets a fixed light level (block light, sky light). The part is bright even in a cave.billboardmakes the part turn to the player.CENTERturns in every direction.textchanges the text of a text part while it is shown.backgroundchanges the box behind a text. Alpha 0 means no box.
Clicks#
A display has no hitbox, so players click through it. Give it one with hitbox, then listen with onClick:
MotionDisplay chest = model.spawn(motion, location)
.hitbox(1f, 1f); // 1 block wide, 1 block high
chest.onClick(click -> {
if (click.rightClick()) {
model.play(chest, "open");
}
});
- The box is a vanilla Interaction entity, sent with packets like the display. Players need no mod.
hitbox(width, height)puts the bottom center of the box at the display location. Its name ismain.- For more boxes, give each one a name and an offset:
hitbox("lid", 0.8f, 0.3f, 0, 0.6, 0). The offset turns with the display. onClickruns on the main thread, so you can use the Bukkit API there.click.player()is who clicked,click.hitbox()is the name of the box,click.leftClick()is true for a left click.- The box stays upright and does not follow animations. It moves with
teleport. - Only players who see the display can click it. Clicks from too far away are ignored.
Find the part names#
display.parts() gives the names of all parts, in the order you added them.
Example#
example/src/main/java/org/jan1k/displaymotion/example/ExamplePlugin.java builds a 53 part windmill. Run it with /dm windmill.
To build models in an editor instead, see Importing.