---
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
```