Making mob(s) revolve around the player

I'm currently working on adding various kinds of special effects and items in minecraft, through command blocks, and have just encountered an obstacle I can't think of a way to bypass.

My idea was to make a piece of armour or potion that gives the player a "shield" of revolving items, say swords. My plans is to make a series of armourstands each holding an item and then teleporting around the player.

So far I've managed to make a circle of villagers that follow the player around when wearing a specific chestplate with the following command

execute at @e[type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] run tp @e[type=villager,nbt={CustomName:"{\"text\":\"Bobby\"}"}] ^ ^ ^2

This way, 8 villagers will all tp to a specific spot relative to the players direction, so they follow smoothly when I walk or look around. Now, I'd like to make them turn around the player as if they are orbiting the player. Preferably also facing away from the player at all times.


Solution 1:

After trying a lot of clever things, it seems like the dumbest solution is the best: hardcoding coordinates relative to the player and teleporting from one fixed (relative) location to the other.

I've made a spreadsheet for the values you need: https://docs.google.com/spreadsheets/d/1aeKocOPtnRd4cCeWyxKb0-1F2oco1pBxYRgJDouFm8s

How to use it: Sadly there seems to be no way to only make two cells of a spreadsheet editable (see my webappsSE question), so I had to set it to view-only to prevent vandalism. That means that you'll have to have a Google account and go to File->"Make a copy" and then edit the three input cells in your copy.

In the first cell at the top left, enter the number of target points you want the rotating entities to have. For example, if you enter "8", then it gives you coordinates for a point to the east of you, then southeast, then south and so on, point 8 is the same as point 0, all have the same distance from you.
With the second cell, you can make the rotating entities skip a number of positions, this would help if you e.g. want one entity to go to points 1, 4 and 7, another to point 2, 5 and 8 and another to point 3, 6 and 9, to make the animation look a bit less repetitive.
The third cell is a multiplier. The number you enter is the radius around the player that the rotating mobs will have.

Now to applying it to Minecraft: Of each number pair (for example columns D+E, F+G etc.) one is the X coordinate relative to the player, one is Z.
Example: 8 coordinate pairs, no coordinates skipped (entering 1 in the second cell) and a distance of 2, after creating a dummy scoreboard "phase":

scoreboard players add @p phase 1
scoreboard players set @p[scores={phase=8..}] phase 0
execute at @p[scores={phase=0}] run tp @e[type=armor_stand] ~2 ~ ~0
execute at @p[scores={phase=1}] run tp @e[type=armor_stand] ~1.414213562 ~ ~1.414213562
execute at @p[scores={phase=2}] run tp @e[type=armor_stand] ~0 ~ ~2
execute at @p[scores={phase=3}] run tp @e[type=armor_stand] ~-1.414213562 ~ ~1.414213562
execute at @p[scores={phase=4}] run tp @e[type=armor_stand] ~-2 ~ ~0
execute at @p[scores={phase=5}] run tp @e[type=armor_stand] ~-1.414213562 ~ ~-1.414213562
execute at @p[scores={phase=6}] run tp @e[type=armor_stand] ~0 ~ ~-2
execute at @p[scores={phase=7}] run tp @e[type=armor_stand] ~1.414213562 ~ ~-1.414213562

This is of course way too fast (one rotation every 0.4 seconds), so you'll want to tweak the numbers and probably also use ranges for the scoreboard selectors. You can also narrow down the selection of armour stands and use multiple, each with their own starting point for the circle and everything else shifted over.

Keep in mind that teleporting is animated, so if you e.g. play in 60fps and teleport every tick, you will see two additional locations in between the ones you entered. These are of course straight lines and not parts of your circle, but with just six teleport targets, you should be practically unable to see a difference when looking from the center, since the radius for them is 1.77, pretty close to 2. For bigger radii and an outside view, you might want to choose more points.


After this dumb solution, here some more clever ones that have at least potential, but have their own disadvantages:

You can teleport every rotating entity in front of you and then make it face you and teleport around you using angular relative motion (^), but you need a factor involving Pi to move it closer to you every step, otherwise it spirals outwards. The problem is that you have to teleport it back to the starting point and then a variable number of steps around you in every tick, which causes more lag and is a less flexible solution.

There was also a nice concept of boats in boats in boats once, where the passenger boats were offset, creating nice circular animations, but sadly it doesn't seem to work anymore in the latest version. It's still cool, so here's a video:

Solution 2:

Idea:

What if you summon an armor stand at the player and run this every tick: (Commands for 1.16)

/execute as @e[type=armor_stand,tag=Spinning] at @s run tp @s ~ ~ ~ ~25 ~

Then use your existing command to teleport the villagers but use it on the armor stand

/execute at @e[type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] as @e[type=armor_stand,tag=Spinning] run tp @e[type=villager,nbt={CustomName:"{\"text\":\"Bobby\"}"}] ^ ^ ^2

This works because you said the villagers rotate when you look meaning if I set them to a rotating entity they will rotate with the entity.

This is untested but is the best thing I could think of :)


Edit: Here are the commands you will need:

Run in a Repeating, Always Active, Command Block followed by Chain, Always Active, Command Blocks: (Order Counts, Top to Bottom)

/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run summon armor_stand ~ ~ ~ {Invisible:1b,Tags:["Spinning"]}
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run summon villager ~ ~ ~ {NoAi:1b,Tags:["Spin"],CustomName:'{"text":"Bobby"}'}
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run summon villager ~ ~ ~ {NoAi:1b,Tags:["Spin1"],CustomName:'{"text":"Bobby"}'}
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run summon villager ~ ~ ~ {NoAi:1b,Tags:["Spin2"],CustomName:'{"text":"Bobby"}'}
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run summon villager ~ ~ ~ {NoAi:1b,Tags:["Spin3"],CustomName:'{"text":"Bobby"}'}
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run tag @s add Checked
/execute as @e[tag=Checked,type=player,nbt={Inventory:[{Slot:102b,id:"minecraft:air"}]}] at @s run tag @s remove Checked
/execute as @e[tag=!Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run tag @s add Checked
/execute as @e[tag=Checked,type=player,nbt={Inventory:[{Slot:102b,tag:{display:{Name:"{\"text\":\"Suit of Villagers\",\"color\":\"gold\",\"bold\":\"true\"}"}}}]}] at @s run tp @e[distance=5,tag=Spinning] ~ ~ ~
/execute as @e[tag=Spinning] at @s run tp @s ~ ~ ~ ~15 ~
/execute as @e[tag=Spinning] at @s run tp @e[distance=..5,tag=Spin] ^ ^ ^2
/execute as @e[tag=Spinning] at @s run tp @e[distance=..5,tag=Spin1] ^ ^ ^-2
/execute as @e[tag=Spinning] at @s run tp @e[distance=..5,tag=Spin2] ^2 ^ ^
/execute as @e[tag=Spinning] at @s run tp @e[distance=..5,tag=Spin3] ^-2 ^ ^
/execute as @e[tag=Spin] at @s rotated as @e[distance=..5,tag=Spinning] run teleport @s ~ ~ ~ ~ ~
/execute as @e[tag=Spin2] at @s rotated as @e[distance=..5,tag=Spinning] run teleport @s ~ ~ ~ ~-90 ~
/execute as @e[tag=Spin1] at @s rotated as @e[distance=..5,tag=Spinning] run teleport @s ~ ~ ~ ~180 ~
/execute as @e[tag=Spin3] at @s rotated as @e[distance=..5,tag=Spinning] run teleport @s ~ ~ ~ ~90 ~
/execute as @e[tag=Spinning] at @s unless entity @e[type=player,tag=Checked,distance=..5] run kill @e[tag=Spinning]
/execute as @e[tag=Spinning] at @s unless entity @e[type=player,tag=Checked,distance=..5] run kill @e[tag=Spin,distance=..5]
/execute as @e[tag=Spinning] at @s unless entity @e[type=player,tag=Checked,distance=..5] run kill @e[tag=Spin1,distance=..5]
/execute as @e[tag=Spinning] at @s unless entity @e[type=player,tag=Checked,distance=..5] run kill @e[tag=Spin2,distance=..5]
/execute as @e[tag=Spinning] at @s unless entity @e[type=player,tag=Checked,distance=..5] run kill @e[tag=Spin3,distance=..5]

I am having problems summoning the entities, but when I tested the spinning it worked. (I edited it so I think it works now, new edits untested)

On the command:
/execute as @e[tag=Spinning] at @s run tp @s ~ ~ ~ ~15 ~
You can adjust the 15 to make it go faster or slower. A higher number means faster, a lower one means slower.

Technical Stuff: This works using an armor stand that is constantly rotated. I execute from that armor stand and teleport the villagers to those relative coordinates. To make them face away from the player I made their facing coords relative to the armor stand that is teleported to the player.

Why I made this: I was working on a similar issue but instead of villagers, it was invisible charged creepers for a cool power effect.

Flaws: When two players get too close to each other it breaks. Takes lots of command blocks. Slight lag if you are playing on a potato.