--- title: 僅用3條指令實現「射擊箭矢」 tags: motion, math --- # 速度向量(Motion) ### 何謂 速度向量(Motion) ? 在Minecraft是指 <font color=blue>實體當前tick的「各方向(x,y,z)之速度」</font>(單位是1 m/tick 或 1 block/tick), 實際案例如:玩家下墜時會有下墜的速度、箭矢朝向前方飛行的速度...等, 而 當前tick 與 下一個tick 的Motion會受到MC內置作用力(重力、摩擦力、空氣阻力)而變動。 空間向量係屬高中數學範疇,但其實以國中的座標程度即可理解其概念: ![](https://truth.bahamut.com.tw/s01/202005/c8b573300cda58845833bcdff453c08a.PNG) > * MC速度向量變化規則參見:實體─運動 - Minecraft Wiki > * 數學大佬請見此篇:关于修正并改进xwj的MC实体运动公式—适用于MC中所有实体 > ( ⚠ 請注意:以上這兩個網址對於 此篇內容 完全不重要 ⚠ ) --- ### 如何修改 速度向量(Motion) ? 可藉由修改生物NBT的 {Motion:[x,y,z]} 來做出修改Motion值的行為, 而用來修改生物NBT的指令主要會使用下方兩種: #### 一、[ /data ... ] <font color=red>ex.</font> /data merge entity @e[type=creeper,limit=1,sort=nearest] {Motion:[0.0,1.0,0.0]} ( 使用data merge修改Motion時需注意:數值必須全為整數[0,1,0] 或是 小數[0.0,1.0,0.0] ) #### 二、[ /execute store result entity ... ] <font color=red>ex.</font> /execute store result entity @e[type=pig,limit=1] Motion[1] double 1 run ... 1. run後方執行能取得數值的相關指令,如/scoreboard players get ... 2. 儲存類別必須使用「double (雙精度浮點數)」:用來儲存小數的資料型態(d) > 有關於data指令的詳細介紹請詳見:[指令/data - Minecraft Wiki](https://ref.gamer.com.tw/redir.php?url=https%3A%2F%2Fminecraft-zh.gamepedia.com%2F%E5%91%BD%E4%BB%A4%2Fdata) > 有關於execute指令的詳細介紹請詳見:[指令/execute - Minecraft Wiki](https://ref.gamer.com.tw/redir.php?url=https%3A%2F%2Fminecraft-zh.gamepedia.com%2F%E5%91%BD%E4%BB%A4%2Fexecute) --- ### 角度與向量的關係 --- # 如何將視角(Rotation)轉換為(Motion) 藉由Minecraft-Java版於1.13新加的「局部座標(^)」功能, 可以讓玩家快速鎖定出視角前方的位置。 (ex. `/execute positioned ~ ~1.6 ~ run particle flame ^ ^ ^1 0 0 0 0 1`) 以下會祭出3種做法,挑一個你喜歡的做法拿去用吧!!!? --- ## 一、制式版 ── 空間座標差: 在「高中數學的空間向量」中,學校會教導我們使用「座標差」的方式來計算向量: 藉由抓出前方1格的座標和玩家自身原本的座標,以求出「座標差」=「向量」。 ![](https://truth.bahamut.com.tw/s01/202005/00e95940c822c63da16c7fdff2a1ac56.JPG?w=1000) 意旨必須要先在玩家前方r格處生成一隻任意實體,並使用指令取得目標實體的座標: `/execute as (目標) store result score @s (記分板) run data get entity @s Pos[i]` 這條指令要執行6次,來儲存目標實體和玩家實體的位置, 並經過3次記分板的operation相減(目標點座標 - 玩家座標),最後才可得出x,y,z之向量, 再將計算出來的結果儲存回Motion的標籤中(執行3次),方可完成角度轉換向量之設定: `/execute as (目標) store result entity @s Motion[i] double 1 run scoreboard players get @s (記分板)` 以此種制式作法最少需要:1個實體、2個記分板、以及執行3+4*3+1=16條指令完成此效果, 下面展示此種(第一種)之指令做法 ── 箭矢射擊(給予箭矢Motion): 1. 先行生成兩項記分板 ```coffeescript= # 儲存目標點座標用: scoreboard objective add object dummy # 儲存箭矢座標用: scoreboard objective add arrow dummy ``` 2. 以玩家(自己)執行此函數: ```coffeescript= # 於玩家視角前方一格生成目標點(object): execute positioned ~ ~1.6 ~ run summon armor_stand ^ ^ ^1 {Tags:["object"],Invisible:1,NoGravity:1} # 生成箭矢並執行 Motion的設定函數: summon arrow ~ ~1.6 ~ execute positioned ~ ~1.6 ~ as @e[type=arrow,limit=1,sort=nearest] run function (自訂3.的函數名稱) ``` 3. 讓箭矢執行上述轉換步驟: ```coffeescript= #計算Motion[0] (x速度向量) 之值 #取得目標點座標(x2),並放大1000倍(用來儲存小數點) execute store result score @s object run data get entity @e[tag=object,limit=1] Pos[0] 1000 #取得箭矢座標(x1),並放大1000倍(用來儲存小數點) execute store result score @s arrow run data get entity @s Pos[0] 1000 #取得向量(x2-x1) scoreboard players operation @s object -= @s arrow #將算出之向量值存回Motion (x),並乘上0.001以還原倍率 execute store result entity @s Motion[0] double 0.001 run scoreboard players get @s object #計算Motion[1] (y速度向量) 之值 execute store result score @s object run data get entity @e[tag=object,limit=1] Pos[1] 1000 execute store result score @s arrow run data get entity @s Pos[1] 1000 scoreboard players operation @s object -= @s arrow execute store result entity @s Motion[1] double 0.001 run scoreboard players get @s object #計算Motion[2] (z速度向量) 之值 execute store result score @s object run data get entity @e[tag=object,limit=1] Pos[2] 1000 execute store result score @s arrow run data get entity @s Pos[2] 1000 scoreboard players operation @s object -= @s arrow execute store result entity @s Motion[2] double 0.001 run scoreboard players get @s object #刪除目標點,結束執行 kill @e[tag=object,limit=1] ``` 感覺這種方式做成指令好攏長... 而且有好多的重複性指令... 是否有更快的計算方式呢 ? --- ## 二、改良版 ── 歸零取座標: 我們既然只是要取得「向量值」,並不一定需要從兩個座標才能求出差值, 換個想法,我們只是需要「往前移動的位移量」, 可以產生一個大膽的想法:從原點(0,0,0)往前方移動後的座標(dx,dy,dz)就是我們要的向量值 ![](https://truth.bahamut.com.tw/s01/202005/59b4cd660f81e7b315dc6a14197c13dc.PNG) 我們只需要用一個實體,從原點照著玩家的角度往前移動, 再將此實體的座標值(Pos)轉移到Motion就好。 以此種改良作法最少需要:1個實體、0個記分板、以及執行5+3+1=9條指令完成此效果, 下面展示此種(第二種)之指令做法 ── 箭矢射擊(給予箭矢Motion): 1. 以玩家(自己)執行此函數: ```coffeescript= #於(0,0,0)生成目標點(object): summon armor_stand 0.0 0.0 0.0 {Tags:["object"],Invisible:1,NoGravity:1} #將目標點往前移動一格: execute rotated as @s positioned 0.0 0.0 0.0 run tp @e[tag=object,limit=1] ^ ^ ^1 #生成箭矢並執行 Motion的設定函數: summon arrow ~ ~1.6 ~ execute positioned ~ ~1.6 ~ as @e[type=arrow,limit=1,sort=nearest] run function (自訂2.的函數名稱) ``` 2. 讓箭矢執行上述轉換步驟: ```coffeescript= #將目標點的座標,存入箭矢的Motion中: execute store result entity @s Motion[0] double 1 run data get entity @e[tag=object,limit=1] Pos[0] execute store result entity @s Motion[1] double 1 run data get entity @e[tag=object,limit=1] Pos[1] execute store result entity @s Motion[2] double 1 run data get entity @e[tag=object,limit=1] Pos[2] #刪除目標點,結束執行 kill @e[tag=object,limit=1] ``` 重複性指令還是有點多... 是否還有辦法優化呢 ? --- ## 三、優化版 ── 歸零取座標 (極為重要的優化技巧): - 其實我們可以將目標點保留,而不需一直刪除 (要使用的時候,善用execute的特性(rotated + positioned)把目標點歸零即可) - Minecraft JE於1.14版本加入了data modify之功能,可以直接一次把整個Pos搬到Motion (詳見data指令介紹:[指令/data - Minecraft Wiki](https://ref.gamer.com.tw/redir.php?url=https%3A%2F%2Fminecraft-zh.gamepedia.com%2F%E5%91%BD%E4%BB%A4%2Fdata) ) - 根據波蘭研究,藥水效果雲(area_effect_cloud)的效能耗量比盔甲架(armor_stand)少很多 (比較影片詳見:[Stands vs Clouds](https://ref.gamer.com.tw/redir.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRKXzWGQfIcg) ) 以此種優化作法最少需要:1個實體、0個記分板、以及執行3條指令完成此效果, 下面展示此種(第三種)之指令做法 ── 箭矢射擊(給予箭矢Motion): 1. 事先執行一次性設定(之後不會再執行此函數): ```coffeescript= #生成目標點(object): summon area_effect_cloud 0.0 0.0 0.0 {Tags:["object"],Duration:2147483647} #載入區塊,確保效果雲不會消失: forceload add -1 -1 0 0 ``` 2. 以玩家(自己)執行此函數: ```coffeescript= #生成箭矢: summon arrow ~ ~1.6 ~ #將目標點從(0,0,0)往前移動一格: execute rotated as @s positioned 0.0 0.0 0.0 run tp @e[tag=object,limit=1] ^ ^ ^1 #將目標點的座標,存入箭矢的Motion中: execute positioned ~ ~1.6 ~ run data modify entity @e[type=arrow,limit=1,sort=nearest] Motion set from entity @e[tag=object,limit=1] Pos ```