Skip to content

Instantly share code, notes, and snippets.

@hugmanrique
Last active October 22, 2022 02:30
Show Gist options
  • Save hugmanrique/1bd273e3b20b419f625c437db61b7ff9 to your computer and use it in GitHub Desktop.
Save hugmanrique/1bd273e3b20b419f625c437db61b7ff9 to your computer and use it in GitHub Desktop.
Small ArmorStand animation created with the Bukkit API
public class SwordAnimation {
// Config variables
private static final float SEPARATOR = 2;
private static final float RAD_PER_SEC = 1.5F;
private static final float RAD_PER_TICK = RAD_PER_SEC / 20F;
private Location center;
private double radius;
private List<ArmorStand> swords;
public SwordAnimation(Location center, double radius) {
this.center = center;
this.radius = radius;
swords = Lists.newArrayList();
}
public void start(JavaPlugin plugin) {
for (double angle = 0; angle < Math.PI * 2; angle += SEPARATOR) {
spawnStand(angle);
}
new BukkitRunnable(){
int tick = 0;
public void run() {
++tick;
for (int i = 0; i < swords.size(); i++) {
ArmorStand stand = swords.get(i);
Location loc = getLocationInCircle(RAD_PER_TICK * tick + SEPARATOR * i);
// Entity falling bug
stand.setVelocity(new Vector(1, 0, 0));
stand.teleport(loc);
}
}
}.runTaskTimer(plugin, 0L, 1L);
}
private void spawnStand(double angle) {
Location loc = getLocationInCircle(angle);
ArmorStand stand = loc.getWorld().spawn(loc, ArmorStand.class);
// TODO Customize by hiding, adding items, rotation...
swords.add(stand);
}
private Location getLocationInCircle(double angle) {
double x = center.getX() + radius * Math.cos(angle);
double z = center.getZ() + radius * Math.sin(angle);
return new Location(center.getWorld(), x, center.getY(), z);
}
}
@xxBeyond
Copy link

I know this is old, but I'm new to using armor stands in this manner. in the chance you see this, how exactly would you execute the code to get everything started?
Also how can i remove the armor stand after a period of time?

@hugmanrique
Copy link
Author

The start method calls spawnStand in a loop, which calls the Bukkit method World#spawn(Location, Class<? extends Entity>). That tells Bukkit you want to spawn an entity of the given type (in this case an ArmorStand) at the given location. Next, the start method creates a repeating task that will move the stands around a circle. You could save the return value of the task (a BukkitRunnable) in a field so you can later cancel it to stop the animation (you would also need to despawn the stands by calling Entity#remove()).

To start the whole thing, your plugin needs to create an instance of SwordAnimation with the appropriate parameters.

@mxuricerm
Copy link

If you have two locations, is it possible to calculate the locations between the given locations?

@hugmanrique
Copy link
Author

hugmanrique commented Feb 6, 2022

Considering the context of this gist, I don't know whether "between" means (1) the location whose distance to two other locations is equal, or, (2) given two angles in a circle, the location in the circle whose argument is the mean of both angles.

For (1), we can compute the arithmetic mean of both locations (say a, b) as

final var midpoint = a.add(b).multiply(0.5);

Note that this method doesn't account for overflow; this is unlikely to happen in a Minecraft world and is left as an exercise.

For (2), given the center c and the two locations a, b, find the midpoint as above, obtain the segment from the center to the midpoint, and scale it by the radius of the circle:

final var center = c.toVector();
final var diff = midpoint.toVector().subtract(center);
final var vect = center.add(diff.normalize().multiply(radius));
final var location = vect.toLocation(c.getWorld());

This method throws if the two points are antipodal (the normalize call divides by zero).

I wrote this on the fly and it isn't optimized, but it should work fine. If performance is a huge concern, avoid Location and Vector objects and handle the math on doubles yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment