Rotate entities arround circle

Discussion in 'Spigot Plugin Development' started by QuestPx, Jul 26, 2018.

  1. Hello. I would like to do the same as on video. I know that for this you need to get the cosine of x and the sine of y, but I get to rotate only one thing, the problem is that if there are a lot of objects, then they rotate all in one location.
     
  2. What I don't understand is how it only doesn't work if there are lots of objects, since the problem should occur with just a few too.
    But, if I understood right, you'll just have to apply an offset to each item.

    Assuming you know the item count, the offset can be calculated by dividing the perimeter of a circle with the amount of elements:
    Code (Java):
    double offset = 2*PI / itemCount;
    Then, I assume you're currently calculating the position like this:
    Code (Java):
    double x = radius * Math.sin(time);
    double y = radius * Math.cos(time);
    And since the 'time' is likely same for all items, the x and y are also the same. So, in the loop that I assume looks as follows:
    Code (Java):
    for (ArmorStand entity : items) {
        double x = radius * Math.sin(time);
        double y = radius * Math.cos(time);
    }
    You have two choices; either keep track of the index of the armor stand (don't use List.indexOf() though, that's super slow, so it'd be a fori loop), or increment a variable outside the loop. The second one is imo a better choice, as you don't have to switch from enhanced loop (and thus you can easily switch between Set/List or whatever you're using).

    So, to apply the correct offset for 'time', it'd look something like this:
    Code (Java):
    double offset = 2*PI / itemCount;

    double angle = time;
    for (...) { // iterate through items
        double x, y;
        x = radius * sin(angle);
        y = radius * cos(angle);

        // teleport or do whatever you do here

        angle += offset;
    }
    Of course, add the center X/Y of the circle to the x and y of each armor stand, as you probably already do.
     
  3. What value do I need for time? System.currentTimeMillis?
     
  4. The time, or better, the angle is the variable of your algorithm. I would implement it like this: start a loop using Bukkit schedulers repeating each X ticks, every time you increment a counter, multiply it to a float constant (velocity) and there you go, you have your angle variable.
    Code (Java):
    double angle = counter * velocity;
    Example:
    Let's say you want your circle to update rotation each tick and make a complete cicle each second.
    Start a Bukkit scheduler running every 1 tick, make a counter variabile as I said above, your angle variable will be:
    Code (Java):
    double angle = counter * (2 * Math.PI / 20)
     
    #4 JohnnyKPL, Jul 26, 2018
    Last edited: Jul 26, 2018
  5. Could not write the code, I do not really understand what to do?
     
  6. The 'time' in my post is basically the 'angle' in this post:
     
  7. Code (Java):
    new BukkitRunnable(new Runnable() {
        int counter = 0;
        @Override
        public void run() {
            counter++;
            double velocity = 0.2f; // edit your velocity here
            double angle = counter * velocity;
            // sin/cos algorithm HERE
        }
    }).runTaskTimer(plugin, 0, 1); // better if sync
     
  8. Something I do not understand at all
    The result is the following code:
    Code (Java):

    double radius = 1.5;
                Location center = e.getClickedBlock().getLocation().add(0.5, 0.5, 0.5);
                List<Location> locations = new ArrayList<Location>();
                locations = getRotatedLocations(center, radius, 2, true);
                List<Hologram> holograms = new ArrayList<Hologram>();
                for(Location loc : locations) {
                    Hologram hd = HologramsAPI.createHologram(this, loc);
                    hd.appendItemLine(new ItemStack(Material.DIAMOND));
                    holograms.add(hd);
                }
                double velocity = 0.1f;
                double offset = Math.PI;
                Bukkit.getScheduler().scheduleSyncRepeatingTask(this, () -> {
                counter++;      
                double angle = counter * velocity;
                for (Hologram hd : holograms) {
                    double x, y;
                    x = radius * Math.sin(angle);
                    y = radius * Math.cos(angle);
                    Location loc = hd.getLocation();
                    loc.setX(loc.getX() + x);
                    loc.setY(loc.getY() + y);
                    hd.teleport(loc);
                }
                }, 0, 1);
     
     
  9. Please help, the message with the code above
     
  10. it's fine, does it work?
     
  11. +-, not as needed, something incomprehensible :D
     
  12. Could you please write an example with the rotation of two objects each tick, for an object you can take an ArmorStand?
     
  13. Well, your problem is that you are adding coordinates on the wrong way. The result of the sin/cos is an offset position that has to be added to the middle of the circle (in your case is it the player location?). Once you added the offset to the player's location you got your new hologram location.
    Now you have your circle rotating along two axis (X and Y, Z and Y or horizontally X and Z), but you may want the whole circle to rotate according to player's facing. That's more difficult, unfortunately I haven't got enough time now to explain.
     
  14. I need to do just like on video, could not the code help?
     
  15. My getRotatedLocations method:
    Code (Text):


    public static List<Location> getRotatedLocations(Location center, double radius, int amount, boolean directionX) {
            List<Location> locations = new ArrayList<Location>();
            double tau = Math.PI*2;
            double increment = tau / amount;
            for(int i = 0; i <= amount; i++) {
                double angle = i * increment;
                double y = center.getY() + radius * Math.sin(angle);
                double zx;
                if(directionX) {
                zx = center.getX() + radius * Math.cos(angle);
                locations.add(new Location(center.getWorld(), zx, y, center.getZ()));
                } else {
                zx = center.getZ() + radius * Math.cos(angle);
                locations.add(new Location(center.getWorld(), center.getX(), y, zx));
                }
            }
            return locations;
        }
     
     
  16. Hello, could not help? I did not edit your code much, but I can not figure out how to determine the winning object in advance (To winloc after the end of the rotation).
    Code:
    Code (Text):

    public BukkitTask doTheThing(Location loc, int numberOfArmorstands, double speed, double radius, Location winLocation){
            return new BukkitRunnable() {
                List<Hologram> armorstands = new ArrayList<>();
                int numOfAS = numberOfArmorstands,
                    ran = 0,
                    endAtRun = -1;
                double PI_2 = Math.PI*2,
                        rad = radius,
                        spd = speed,
                        currentOffset = 0,
                        asOffset = PI_2/numOfAS;
                Location winLoc = winLocation;
                boolean isRandomed = false;
                Hologram winHologram;
                @Override
                public void run() {
                    Location pLoc = loc.clone().add(0.5, 1, 0.5);
                    if(armorstands.size() < numOfAS) {
                        if(Math.toRadians(currentOffset) >= asOffset*armorstands.size()){
                            Hologram hd = HologramsAPI.createHologram(Main.instance, pLoc);
                            hd.appendItemLine(new ItemStack(Material.DIAMOND));
                            armorstands.add(hd);
                            return;
                        }
                    }
                    if(!isRandomed) {
                        int number = new Random().nextInt(armorstands.size());
                        winHologram = armorstands.get(number);
                        winHologram.clearLines();
                        winHologram.appendItemLine(new ItemStack(Material.EMERALD));
                        isRandomed = true;
                    }
                    int asNum = 0;
                    double cO = Math.toRadians(currentOffset%360d);
                    for(double d = 0; d<PI_2; d+=PI_2/numOfAS){
                        if(asNum >= armorstands.size()) break;
                        double a = d+cO,
                                cos = Math.cos(a),
                                sin = Math.sin(a);
                        Hologram as = armorstands.get(asNum++);
                        Location l = pLoc.clone().add(cos*rad, -sin*rad, 0);
                        l.setDirection(pLoc.toVector().subtract(l.toVector()).normalize());
                        as.teleport(l);
                    }
                    double m = 1d;
                    if(armorstands.size()==numOfAS){
                        if(endAtRun == -1) endAtRun = ran + 20*3 + ThreadLocalRandom.current().nextInt(20*5); // end time = 3 to 8 seconds from now
                        if(ran >= endAtRun){ // if we reached end time
                            if(winHologram.getLocation().distance(winLoc) > 0.3) {
                                endAtRun = ran + 20*3 + ThreadLocalRandom.current().nextInt(20*5);
                            } else {
                            cancel();
                            winHologram.teleport(winLoc);
                            Bukkit.getScheduler().scheduleSyncDelayedTask(Main.instance, () -> {
                            armorstands.forEach(as -> as.delete());
                            armorstands.clear();
                            }, 60);
                            return;
                            }
                        }
                        int remaining = endAtRun-ran;
                        m = .1+remaining/20d/10d; // 0.1 to 0.9 modifier
                    }
                   
                    currentOffset+=spd*m;
                    ran++;
                }
            }.runTaskTimer(Main.instance, 0, 1);
        }
     
    Forgive my English, I'm from Russia
     
  17. The code i posted in the other thread picks winning armorstand by getting the closest armorstand to winning location.
    Do you want to pick winning armorstand differently? Then just pick it and rotate them until the winning armorstand is the closest armorstand to winLocation. Or try googling / think about how to do it more effectively...
     
  18. Yes, I use the distance to winLoc, but can I use the formula to calculate the length and change the speed or the rotation time so that the object will stand up on the winLoc itself at the end?
     
  19. So when rotation ends you want to move armorstand to winlocation? If yes then next time just use google to find answers..