切換
舊版
前往
大廳
主題

【指令】僅用3條指令實現「射擊箭矢」,帶你熟悉何謂「速度向量(Motion)」

雪色 | 2020-05-03 20:57:09 | 巴幣 18 | 人氣 1456

這裡是好久不見的雪色
前期因為學測和準備升學的事情,所以暫時去潛水了(潛水了大概一年多(?),
期間也有進展PVP地圖相關的進度。
(下方文章會透露一點此地圖之內容)

這次挑戰換一種排版方式(感覺我之前的排版方式好醜-3-),希望大家能喜歡.w.。

( 請注意,玩家無法被修改Motion)



你可以在此篇文章學習到什麼 ?

一、理解速度向量(Motion)之用途
二、用指令修改Motion的方式 和 注意事項
三、角度 轉換成 向量 之計算原理
四、以指令實現「向前方射擊箭矢」
五、反思:能否更進一步優化指令?





何謂 速度向量(Motion) ?

在Minecraft是指 實體當前tick的「各方向(x,y,z)之速度」(單位是1 m/tick 或 1 block/tick)
實際案例如:玩家下墜時會有下墜的速度、箭矢朝向前方飛行的速度...等,
而 當前tick 與 下一個tick 的Motion會受到MC內置作用力(重力、摩擦力、空氣阻力)而變動。

空間向量係屬高中數學範疇,但其實以國中的座標程度即可理解其概念:

( 請注意:以上這兩個網址對於 此篇內容 完全不重要 )




如何修改 速度向量(Motion) ?

可藉由修改生物NBT的 {Motion:[x,y,z]} 來做出修改Motion值的行為,
而用來修改生物NBT的指令主要會使用下方兩種:

一、[ /data ... ]
ex. /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 ... ]
ex. /execute store result entity @e[type=pig,limit=1] Motion[1] double 1 run ...
1. run後方執行能取得數值的相關指令,如/scoreboard players get ...
2.  儲存類別必須使用「double (雙精度浮點數)」:用來儲存小數的資料型態(d)






如何將視角的角度轉換成 速度向量(Motion) ?

藉由Minecraft-Java版於1.13新加的「局部座標(^)」功能,
可以讓玩家快速鎖定出視角前方的位置。
(ex. /execute positioned ~ ~1.6 ~ run particle flame ^ ^ ^1 0 0 0 0 1)
以下會祭出3種做法,挑一個你喜歡的做法拿去用吧!!!?


一、制式版 ── 空間座標差:

在「高中數學的空間向量」中,學校會教導我們使用「座標差」的方式來計算向量:
藉由抓出前方1格的座標和玩家自身原本的座標,以求出「座標差」=「向量」
意旨必須要先在玩家前方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. 先行生成兩項記分板

#儲存目標點座標用:
scoreboard objective add object dummy
#儲存箭矢座標用:

scoreboard objective add arrow dummy

2. 以玩家(自己)執行此函數:

#於玩家視角前方一格生成目標點(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. 讓箭矢執行上述轉換步驟

#計算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)就是我們要的向量值


我們只需要用一個實體,從原點照著玩家的角度往前移動,
再將此實體的座標值(Pos)轉移到Motion就好。

以此種改良作法最少需要:1個實體、0個記分板、以及執行5+3+1=9條指令完成此效果,
下面展示此種(第二種)之指令做法 ── 箭矢射擊(給予箭矢Motion)

1. 以玩家(自己)執行此函數:

#於(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. 讓箭矢執行上述轉換步驟

#將目標點的座標,存入箭矢的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]

重複性指令還是有點多... 是否還有辦法優化呢 ?
(被第一種的制式作法擺了一道,有沒有很爽阿(?,把第一種流程打完的我感覺真爽... .. .


三、優化 ── 歸零取座標 (極為重要的優化技巧)

1. 其實我們可以將目標點保留,而不需一直刪除
(要使用的時候,善用execute的特性(rotated + positioned)把目標點歸零即可)

2. Minecraft JE於1.14版本加入了data modify之功能,可以直接一次把整個Pos搬到Motion
(詳見data指令介紹:指令/data - Minecraft Wiki )

3. 根據波蘭研究,藥水效果雲(area_effect_cloud)的效能耗量比盔甲架(armor_stand)少很多
(比較影片詳見:Stands vs Clouds )

以此種優化作法最少需要:1個實體、0個記分板、以及執行3條指令完成此效果,
下面展示此種(第三種)之指令做法 ── 箭矢射擊(給予箭矢Motion)

1. 先執行一次性設定(之後不會再執行此函數):

#生成目標點(object):
summon area_effect_cloud 0.0 0.0 0.0 {Tags:["object"],Duration:2147483647}

#載入區塊,確保效果雲不會消失:
forceload add -1 -1 0 0

2. 以玩家(自己)執行此函數

#生成箭矢:
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

(很好,這樣就沒人會用第一種和第二種的做法了,陰謀論通





你可以用 速度向量(Motion) 做出怎麼樣的效果呢 ?

以下偷工商(X,這是我們目前正在製作的PVP地圖的部分內容,
影片中的詳細指令做法與地圖資訊暫不公開,
可以對影片內容進行提問(選擇性答覆) 以及 與我們進行合作洽談(附於影片資訊中)






結語與閒話

總算出來了-3-,浮出來才發現已經潛水一年多...,
在學了程式與演算法之後對於指令執行優化上多了好多的琢磨...,
看到哪種功能耗費太多條指令就開始著手優化,快養成壞習慣了(X

此篇最主要目的還是想傳遞一些「新版指令的方便性」和「優化指令的技巧」,
最近看到大部分人都還是在用【制式作法】,所以就上傳這篇技術文了,
希望能對那些還在研究或是很需要優化的創作者們有幫助。

清華和交通的APCS好虐阿,
交通備取(但看下來是穩上:above 90% ),但清華連備取都沒有..,
(我很想要清華的精美成績通知小卡片的說
如果也有對於APCS組相關考試有問題的也歡迎來問我(X
最近總算可以閒下來了...

(點擊圖片,前往我的歷屆指令文章收錄小屋)

創作回應

雞塊
是大佬 大佬回歸了
2020-05-05 13:03:13
雪色
等你今年入圍TOI 2!的消息(X
2020-05-05 13:36:50
雞塊
今年沒了QQ
2020-05-05 14:03:43
雪色
是指12月的那一場w(X 雖然不知道會不會辦
2020-05-05 14:10:49
追蹤 創作集

作者相關創作

更多創作