Cookbook
Short recipes. Each one is code you can copy. They use motion for your DisplayMotion and location for a Location. See Getting started for how to make them.
Floating item that spins#
Good for a reward, a drop or a shop item. It grows in, then spins and floats.
static final Animation FLOAT = Animations.popIn(20).then(Animations.spin(80).with(Animations.bob(0.3f, 80)));
motion.item(location, ItemStack.of(Material.NETHER_STAR)).play(FLOAT);
Remove with an animation#
A display that just disappears looks cheap. Shrink it first. The code after the comma runs when the animation ends.
display.play(Animations.popOut(10), display::remove);
Follow a player like a pet#
A task moves the display every 2 ticks. The 4 makes the client glide over 4 ticks, so the pet trails a bit behind the player.
MotionDisplay pet = motion.item(spot(player), ItemStack.of(Material.DIAMOND_SWORD)).play(FLOAT);
Bukkit.getScheduler().runTaskTimer(plugin, task -> {
if (pet.isRemoved() || !player.isOnline()) {
task.cancel();
return;
}
pet.teleport(spot(player), 4); // move every 2 ticks, glide over 4, so it trails behind
}, 2L, 2L);
spot is a small helper that picks a place behind the player:
static Location spot(Player player) {
Location spot = player.getLocation();
Vector back = spot.getDirection().setY(0);
if (back.lengthSquared() > 1e-6) {
spot.subtract(back.normalize().multiply(1.2));
}
spot.add(0, 1.6, 0);
spot.setPitch(0f);
return spot;
}
Only one player sees it#
Use this for quest markers or private hints. Nobody sees the display until you call show.
motion.item(location, ItemStack.of(Material.EMERALD))
.visibleByDefault(false)
.show(player);
Hide from one player#
display.hide(player);
display.show(player); // back again
Label that faces the player#
A billboard text turns to look at every player, so it is always readable.
motion.text(location, Component.text("Spawn", NamedTextColor.GOLD))
.billboard(Display.Billboard.CENTER)
.play(Animations.popIn(15));
Change a text later#
label.text(Component.text("Open now", NamedTextColor.AQUA)); // label is a text display you spawned before
Glowing outline#
display.glow(Color.AQUA); // null turns it off
Always bright, even in a cave#
By default a display is lit by the world, so it is dark in a cave. A fixed light level fixes this.
display.brightness(new Display.Brightness(15, 15));
Mirror a part#
A negative scale flips a part on that axis.
Pose.IDENTITY.withScale(-1f, 1f, 1f)
A hop#
static final Animation HOP = Animations.slide(0f, 1f, 0f, 6, Ease.OUT)
.then(Animations.slide(0f, -1f, 0f, 6, Ease.IN));
then starts each part where the last ended, so the second slide brings it back down.
A button you can click#
Right click presses it and counts. Left click breaks it.
static final Animation PRESS = Animation.keyframes()
.at(0, Pose.IDENTITY)
.at(2, Pose.IDENTITY.withOffset(0f, -0.08f, 0f))
.at(6, Pose.IDENTITY)
.build();
int[] clicks = {0};
MotionDisplay button = motion.model(location)
.block("base", Material.POLISHED_BLACKSTONE.createBlockData(), Pose.IDENTITY.withOffset(0f, 0.1f, 0f).withScale(0.9f, 0.2f, 0.9f))
.block("cap", Material.RED_CONCRETE.createBlockData(), Pose.IDENTITY.withOffset(0f, 0.27f, 0f).withScale(0.6f, 0.14f, 0.6f))
.text("label", Component.text("Click me"), Pose.IDENTITY.withOffset(0f, 0.8f, 0f))
.spawn()
.billboard("label", Display.Billboard.CENTER)
.hitbox(1f, 0.5f);
button.onClick(click -> {
if (click.leftClick()) {
motion.effects().shatter(location, Material.RED_CONCRETE.createBlockData());
button.remove();
return;
}
clicks[0]++;
button.play("cap", PRESS).text("label", Component.text("Clicks: " + clicks[0]));
});
Try it in game with /dm button from the example plugin.
Spawn an imported model#
Load the file once. Spawn it every time you need it. See Importing for the full steps.
ImportedModel robot = ImportedModel.load(getDataFolder().toPath().resolve("models/robot.bbmodel"));
MotionDisplay display = robot.spawn(motion, location, 0.5f);
robot.play(display, "walk");
Remove everything when the plugin stops#
You do not need to do anything. The library removes all displays when your plugin disables. To do it by hand, call motion.close().