TransWikia.com

Make an entity follow the player, but offset

Arqade Asked by Mi Taylor on March 19, 2021

Is there a way to use commands to take the Motion data from one entity and merge it onto another? I want to let another entities move in unison with the player. Just copying the Motion tag does not work, because the X and Z motion seems to always be 0.

One Answer

The Motion tag of players is weird. Player movement is controlled partially by the client, partially by the server, there are various checks, anti-cheats, latency handling mechanics, workarounds, etc. From my testing, Motion[0] and Motion[2] only seem to work when you sprint-jump, not when just sprinting, just jumping or any other type of manual movement, otherwise it's always 0.

The workaround is to use Pos instead. But that is much more complicated than copying the Motion tag from one entity to another, because if you copied it directly, the entity would just get teleported to where you are instead of relative to you. Of course you could use relative teleports, but for those you would need to hard-code the X, Y and Z distance to you. If that is variable (so a "relative relative teleport", I guess), then you need to do a bit of Maths.

In this example, all armour stands tagged "to_move" will get moved in unison with one player tagged "reference". If you want to have multiple reference players, you'll have to adjust the system, for example with IDs, by selecting the nearest player to each armour stand or something else. I did not do this here.

Initial setup:

/scoreboard objectives add x dummy
/scoreboard objectives add y dummy
/scoreboard objectives add z dummy
/scoreboard objectives add x_distance dummy
/scoreboard objectives add y_distance dummy
/scoreboard objectives add z_distance dummy

Whenever you want to store the relative distance between player and armour stand:

execute as @e[type=armor_stand,tag=to_move] store result score @s x_distance run data get entity @s Pos[0] 50
execute as @e[type=armor_stand,tag=to_move] store result score @s y_distance run data get entity @s Pos[1] 50
execute as @e[type=armor_stand,tag=to_move] store result score @s z_distance run data get entity @s Pos[2] 50
execute as @p[tag=reference] store result score @s x run data get entity @s Pos[0] 50
execute as @p[tag=reference] store result score @s y run data get entity @s Pos[1] 50
execute as @p[tag=reference] store result score @s z run data get entity @s Pos[2] 50
scoreboard players operation @e[type=armor_stand,tag=to_move] x_distance -= @p x
scoreboard players operation @e[type=armor_stand,tag=to_move] y_distance -= @p y
scoreboard players operation @e[type=armor_stand,tag=to_move] z_distance -= @p z

Whenever you want to update the armour stand's position (so usually every tick):

execute store result score @e[type=armor_stand,tag=to_move] x run data get entity @p[tag=reference] Pos[0] 50
execute store result score @e[type=armor_stand,tag=to_move] y run data get entity @p[tag=reference] Pos[1] 50
execute store result score @e[type=armor_stand,tag=to_move] z run data get entity @p[tag=reference] Pos[2] 50
execute as @e[type=armor_stand,tag=to_move] run scoreboard players operation @s x += @s x_distance
execute as @e[type=armor_stand,tag=to_move] run scoreboard players operation @s y += @s y_distance
execute as @e[type=armor_stand,tag=to_move] run scoreboard players operation @s z += @s z_distance
execute as @e[type=armor_stand,tag=to_move] store result entity @s Pos[0] double .02 run scoreboard players get @s x
execute as @e[type=armor_stand,tag=to_move] store result entity @s Pos[1] double .02 run scoreboard players get @s y
execute as @e[type=armor_stand,tag=to_move] store result entity @s Pos[2] double .02 run scoreboard players get @s z

Explanation:

The setup just creates scoreboards, that's easy.

The first three commands in the second code block let each armour stand store its current coordinates in its own x, y and z scoreboards… but not quite. You actually need less commands in total if you write it into x_distance, y_distance and z_distance instead and modify those values later. Otherwise x, y and z would get only used for copying to x_distance, y_distance and z_distance and later get overwritten anyway.
I use a scale factor here, because scoreboards are integers, so storing the position in them directly would round them down to the next lowest whole number, which would make the movement very choppy. The higher the scale factor is, the smoother the motion becomes, but at a scale factor above ≈71.58, you can get overflow issues when you are near the world border. The larger the scale factor, the smaller the part of the world is in which the system actually works correctly. With a factor of 50, it works everywhere and you can later conveniently multiply with 0.02. It's also precise enough that it's no longer noticeable that there's any limit to the precision at all.
The next three commands store the player's coordinates in their x, y and z scoreboards, this time for real and with the same scale factor. You actually need to use the same scale factor for armour stands and players, otherwise this system does not work correctly. I'm using /execute as here to avoid having to use @p[tag=reference] twice, that's a tiny performance improvement.
The last three commands in that code block subtract the player's X coordinate from the armour stand's X coordinate and the same for Y and Z, then store that in the armour stand's x_distance, y_distance and z_distance scores. For example, if you stand at X=10 and the armour stand is at X=15, the armour stand's x_distance score will be 15-10=5.

The first three commands in the last code block store the player's current position in each armour stand's x, y and z scores, as a starting point for the calculation.
The next three commands increase each armour stand's x, y and z scores by their x_distance, y_distance and z_distance scores, resulting in the position that they should get teleported to.
Finally, the calculated position gets copied to the armour stand's Pos tag, which instantly updates its actual position.

This system is actually really flexible:

  • If you want the armour stand to only update its position at certain times instead of every tick, you can just run the commands in the third code block less often and it will still work fine (unlike a direct copy of Motion would, even without the weird side effects that that has).
  • If you want to pause moving, you can just not run the commands. Afterwards, you can either decide to keep applying the same offset again by simply continuing to run the commands or you can use the new distance between player and armour stand, by simply running the commands in the second code block again.

Answered by Fabian Röling on March 19, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP