最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

科學的Minecraft:速度?重力加速度?自由下落極限速度?

2020-09-13 21:35 作者:Nickid2018  | 我要投稿

在現(xiàn)實生活中,我們把物體在某段時間內(nèi)的位移與這段位移所用時間的比值叫做“速度”。那么,在一個虛擬的世界中,是否有速度呢?

警告:這篇文章會有大量的代碼以及原理講解,請慎重觀看!

這篇文章涉及到的表觀現(xiàn)象:各種有關(guān)于移動的掛,MC中速度的表示,MC中重力加速度的計算,自由下落極限速度

一.Minecraft的速度定義

首先,MC里面定義了兩種測量速度的字段:deltaMovement與speed。

deltaMovement是一個矢量,它決定了在這1tick內(nèi)實體運動方向和距離,用它除以1tick就是平均速度。

v平均 = deltaMovement / 1t = deltaMovement / 0.05 (TPS=20)

speed則是標量,相當于平均速率的量度,是一個計算平均速率的參數(shù)。

兩個字段的定義


二.非玩家的LivingEntity移動

首先,我們要了解speed到底儲存的位置——也就是Attribute里面。Attribute里面儲存了一個MOVEMENT_SPEED,也就是移動速度。而我們得到的速度則是通過直接讀取speed字段,而這個字段正是由Attribute得出的。

由于代碼又看不到具體調(diào)用,只好又修改LivingEntity.setSpeed,看看它的堆棧。

插入dumpStack的位置

運行/de:debuginsert net.minecraft.world.entity.LivingEntity 2036 false dump插入代碼,得到了以下堆棧:

Server Thread的堆棧
Client Thread的堆棧,只有這一種堆棧是Client的

通過Server的堆棧我們可以看出生物(Mob)的運動都是由MoveControl處理的,下面先講一下Mob和LivingEntity的移動方式。

move的計算就在它的tick方法里面。這個代碼比較長,所以只會講一部分。

//注:其實生物運動分為navigation->move(X,Z軸上的移動)->look->jump(Y軸上的移動),這里只找出了有關(guān)于speed的部分

超長的代碼,你喜歡嗎

由于代碼過長,我參考了1.15.2的MDK源碼作為參考,下面我只會講這一大部分里面的STRAFE(你沒聽錯,這個單詞意思是掃射)部分,這里聯(lián)系到了一會要講的move。

首先,獲取到speed(65行),同時計算得出實際的行進速度(66行)。之后獲得生物根據(jù)面朝方向的向前(67行)和左右方向(68行)上的移動距離,計算出實際上的位移(69-72行),獲得這1tick內(nèi)能前進的距離的參數(shù)(73行,這時候f5單位是s^-1)。接下來應該是獲取到將要走到的位置,檢測是否能到達(74-86行)。之后,設(shè)置speed(87行),傳遞X,Z方向上的位移(88-89行),設(shè)置狀態(tài)為WAITING(90行)。

這里我們看到了怎么設(shè)置speed,但是沒有看到move,不用著急,這只是我們了解它的第一步。


LivingEntity#aiStep 2242行是Mob#serverAiStep調(diào)用 2249行則是我們要的travel

緊接著,我們發(fā)現(xiàn)Mob這里的邏輯完成之后又調(diào)用了上一級的LivingEntity.travel,這個就是我們下一步說的移動的關(guān)鍵之一。

還記得MoveControl里面的“傳遞X,Z方向上的位移”嗎,我們可以看到,這個方法的傳入值就有xxa和zza,那么我們在這里找到move的可能性就很大了(ArmorStand沒有繼承Mob而直接繼承了LivingEntity,它是一個例外)

由于這個方法有200行,所以我只能講解一部分,發(fā)出一部分代碼。

這是travel在非飛行狀態(tài),玩家沒有掉到巖漿里的默認處理

下面對這段代碼進行解釋:

獲取腳下方塊(1889行),之后得到行進阻力(1890-1891行),通過moveRelative方法進行阻力行進運算(1892行)。對移動加上攀爬時的Y軸方向速度(1893行),將這些值代入,move中進行實際的移動(1894行)。(這些為該刻處理運動的部分)

獲取現(xiàn)在的移動速度(1895-1899行),進行Y軸速度的判斷:

? ? 1.具有漂浮效果,將Y軸速度改為 漂浮等級+1-原Y軸速度

? ? 2.如果重力存在,將Y軸速度向下加0.08(沒有緩降效果)/0.01(具有緩降效果)

? ? 3.在客戶端運行且區(qū)塊未加載,將Y軸速度改為-0.1或0

進行此判斷后重新計算速度,現(xiàn)速度X,Z軸上速度為原先的速度乘以阻力比例,Y軸則是判定結(jié)果乘上0.98。(這里是下一次移動前的準備)

處理進行阻力影響的速度

關(guān)于阻力比例,公式是這樣的:

當實體站于地面,v = speed * (0.21600002 / (blockFriction ^ 3)

當實體在天空中,v = flyingSpeed (0.2)

這個方塊阻力和動摩擦因數(shù)μ差不多,算得的速度應該是speed*(0.6/friction)^3,因為浮點數(shù)誤差,所以原先的0.216變成了0.21600002,不過這對我們得出結(jié)論沒有影響。

下面來看它調(diào)用的最終級方法:move

這個方法定義在Entity里面,也就是所有實體公用的,不像LivingEntity的travel一樣只適用于LivingEntity和它的子類Mob。

由于這里又是150行代碼,我們還是講精華部分。

最重要的代碼往往只需要最精華的部分

下面解讀一下代碼,注意,這里所有的XXX > 1.0E-7D都是在判斷移動發(fā)生,由于浮點數(shù)誤差,計算后的數(shù)據(jù)即使你看的是0,其實還是有很微小的值,用XXX == 0是不能起效果的:

(491和505行:報告狀態(tài),對于移動判斷沒有用處,就是調(diào)試用的)

492-496行:如果阻礙運動參數(shù)大于0,則應用阻礙,并將下一刻的參數(shù)和位移置為0

498行:應用潛行(對于玩家的)

499行:計算碰撞,返回實際的位移

500-503行:如果位移不為0,啟用真正的移動

真正的移動分為了兩部分:第一步,setBoundingBox,設(shè)置包圍盒。此時,該實體的包圍盒已經(jīng)變化了,但是渲染位置不變;第二步,setLocationFromBoundingbox,從現(xiàn)在的包圍盒位置獲取到現(xiàn)在的實體位置,這時候,渲染位置才改變到正確位置。

好了,我們看到了LivingEntity移動的全過程,那么如果不是LivingEntity呢?

三.非LivingEntity的移動

這里我將講這兩個非LivingEntity實體:下落的方塊,點燃的TNT。

1.下落的方塊(FallingBlockEntity)

下落的方塊的移動是寫死到tick里面的,由于又是一大串代碼,還是取其精華。

沒有方塊阻止下落實體的下落的處理

這幾段代碼里面清楚地寫到了move和deltaMovement,可以判斷出:這就是它運動的方式??墒悄憧赡軙枺夯钊苿酉侣浞綁K也會造成位移,這里沒有體現(xiàn)啊?其實,move的第一個參數(shù),MoverType里面定義了不同的移動方式,這種運動方式不需要實體自己計算,活塞推動是MoverType.PISTON,而實體自己移動是SELF。

2.點燃的TNT(PrimedTNT)

點燃TNT的位移是在兩個地方計算:tick和構(gòu)造函數(shù)。

PrimedTNT中的位移

這個代碼有兩個部分,一部分是構(gòu)造函數(shù)的,另一個在tick里面。tick里面的代碼和FallingBlockEntity的代碼大致相同,不再解釋。構(gòu)造函數(shù)中的代碼是點燃TNT的時候,TNT會“蹦一下”,這個的計算就是這里體現(xiàn)的。具體來說,這一刻跳起的高度應該是0.2(浮點數(shù)誤差x3),而X、Z軸上則是通過隨機數(shù)產(chǎn)生一個方向位移0.02。

四.玩家的移動

還記得最上面的那張Client堆棧嗎,我們在那里發(fā)現(xiàn)了Player的蹤跡。我們通過這條線索向下查:

Player改動speed的位置

這里能夠看出,speed是由服務(wù)器端計算的,并將這個值存到Abilities里面發(fā)送回來。

但是,deltaMovement去哪了?

ServerPlayer對tick進行了覆蓋

由于Player是LivingEntity的子類,翻找過后,發(fā)現(xiàn)在服務(wù)器端的ServerPlayer覆蓋了tick并且沒有進行super調(diào)用,而我們普通LivingEntity進行移動正是通過tick調(diào)用aiStep進而調(diào)用travel實現(xiàn)移動的!并且在這個新的tick里面,并沒有aiStep和travel的蹤跡......

那么ServerPlayer是怎么實現(xiàn)移動的??

那么在這個問題解答之前,大家可以想一想飛行掛、快速移動和載具加速這種東西是怎么產(chǎn)生的。并且,如果你用過tweakeroo的靈魂出竅(FreeCamera),在靈魂出竅的時候被推動,你有可能就懸空了......在這些現(xiàn)象的背后,是什么原理?

對,沒錯。因為服務(wù)器端只負責了“移動你”,但是不負責“計算使你運動”,而客戶機端才真正負責了玩家的自身(PLAYER)實體移動。關(guān)于這一點為何這么實現(xiàn),其實也很簡單的道理——因為你可以操控玩家,而其他的實體都靠著物理引擎和AI直接運算。

//注:服務(wù)器端不是什么玩家移動都靠客戶機端,因為活塞移動等計算是在服務(wù)器進行的,因為活塞移動等是服務(wù)器操作,活塞移動同時就進行了move方法調(diào)用。而客戶機端掌握的移動只有PLAYER的自身移動。

為了證明這一點,我們可以去翻找LocalPlayer的源碼。

LocalPlayer#sendPosition,在tick里面有它的調(diào)用 bl4=>是否進行坐標移動 bl3=>是否進行視角移動

在LocalPlayer里面,我們發(fā)現(xiàn)了sendPostion發(fā)送位置,這代表了客戶機計算位置的正確性。為了繼續(xù)證實,我們翻找一下ServerboundMovePlayerPacket的執(zhí)行者,也就是ServerGamePacketListenerImpl。

位置變動的處理部分

由于代碼長度太大,120行左右,就簡單說一下它干了什么:

  1. 檢查數(shù)據(jù)的正確性

  2. 如果玩家還在等待區(qū)塊加載傳輸,則“固定”玩家位置,防止因為客戶端區(qū)塊未加載導致的掉入虛空

  3. 如果玩家是乘客,那么進行加載區(qū)塊緩存

  4. 如果玩家睡眠時的位移超過1,將被強制傳送回床上

  5. 如果在1tick內(nèi)玩家發(fā)送了超過5個移動請求,那么警告“<player> ?is sending move packets too frequently (<packets> packets since last tick)”,并且強制合并為一個包(也就是為什么移動會彈回的原因之一)

  6. 如果玩家不是在進行維度變更,或者鞘翅飛行速度檢查啟用時進行鞘翅飛行,如果速度過快(公式就不給了),將警告“<player> moved too quickly! <x>,<y>,<z>”,并強行傳送回之前的位置(彈回原因二)

  7. 在這之后,進行實際移動,MoverType為PLAYER

  8. 判斷玩家移動的正確性,不正確警告“<player> moved wrongly!”

  9. 處理摔落傷害等數(shù)據(jù)更新

通過這些,我們就能看出Player到底是怎么移動的了。

五.MC的重力加速度到底是多少

通過前文,我們基本上了解了移動是怎么發(fā)生的了,下面就來看看重力加速度到底是多少吧。

首先是LivingEntity里面的分析,抄一下之前的話:

獲取現(xiàn)在的移動速度(1895-1899行),進行Y軸速度的判斷,

如果重力存在,將Y軸速度向下加0.08(沒有緩降效果),

判定結(jié)果乘上0.98。

這樣,我們得出了一個有關(guān)于速度的以tick為自變量的函數(shù),記為F(x)

來個公式推導

由于浮點數(shù)的誤差,這個值很快就會固定到3.92m/tick(實測是3.9200038147008747,因為有浮點數(shù)誤差),也就是78.4m/s,這也就是生物自由下落極限速度。

在280tick(14s)左右達到接近3.92(浮點數(shù)差值小于1E-4)

這時候,如果我們認為下落開始一瞬間不計空氣阻力,那么重力加速度就是x=0時的導數(shù)值

F'(0)=0.07919

也就是約為31.7m/s^2

下面是客戶端的驗證,這里用到的是fabric端,用了miniHUD的功能。

測試的GIF

我們看到,隨著速度越來越接近78.4m/s,速度增長越慢,在78.4m/s徹底停止,也就認證了我們的理論。

然而這就結(jié)束了嗎?并沒有。非生物實體也有類似的公式,像下面這樣:

%5Cbegin%20%7Barray%7D%5C%5C%0Av_n%3D(1-friction)(v_%7Bn-1%7D%2Baccel)%5C%5C%0Av_n%3D(1-friction)v_%7Bn-1%7D%2Baccel%0A%5Cend%20%7Barray%7D

其中friction代表阻力,accel代表“每刻的加速度”。

由上面的公式,可以得到速度公式:

%5Cbegin%20%7Barray%7D%20%5C%5C%0Av(t)%3D%5Cfrac%20%7Baccel%20(1-friction)%7D%20%7Bfriction%7D(1-accel%5Et)%5C%5C%0Av(t)%3D%5Cfrac%20%7Baccel%7D%20%7Bfriction%7D(1-accel%5Et)%0A%5Cend%20%7Barray%7D

終端速度

%5Cbegin%20%7Barray%7D%5C%5C%0Av_%7Bmax%7D%3D%5Cfrac%20%7Baccel%20(1-friction)%7D%20%7Bfriction%7D%5C%5C%0Av_%7Bmax%7D%3D%5Cfrac%20%7Baccel%7D%20%7Bfriction%7D%0A%5Cend%20%7Barray%7D

具體的阻力和“加速度”依照下表,來自Minecraft Wiki“實體”頁面:

標注[注1]使用第二個公式,沒有標注則為第一個

六.有關(guān)于粘液塊的事情

粘液塊是一個能將detlaMovement進行修改的方塊,在你站立于上方時,你本身會有一個向下的“趨勢位移”(在上一篇專欄有講),而粘液塊能修改它,導致你站在粘液塊上時deltaMovement會很歡快地跳動。具體可以看看驗證視頻。

專欄中使用的Minecraft反混淆+動態(tài)修改程序:MCDynamicExchanger,地址https://github.com/Nickid2018/MCDynamicExchanger

數(shù)學公式繪制:MathType? 函數(shù)繪制:幾何畫板

Minecraft:反混淆1.14.4端+動態(tài)修改程序

fabric端:模組是maLiLib,miniHUD,(tweakeroo,litematica,itemscroller,worldedit)

啟動器:HMCL 3.3.172

分配內(nèi)存:512MB

系統(tǒng):Windows 7 32位 運行內(nèi)存2GB

驗證視頻:


如果有任何問題或者文章中有錯誤,可以在評論區(qū)告訴我,我會修改掉錯誤的。

科學的Minecraft:速度?重力加速度?自由下落極限速度?的評論 (共 條)

分享到微博請遵守國家法律
吴川市| 炉霍县| 饶平县| 沭阳县| 小金县| 卢龙县| 瑞昌市| 湛江市| 靖边县| 固阳县| 绥化市| 武城县| 徐闻县| 西贡区| 四平市| 神池县| 巴彦淖尔市| 雷波县| 宝兴县| 夹江县| 昭平县| 重庆市| 吉安市| 云龙县| 包头市| 新蔡县| 秭归县| 钦州市| 金昌市| 屏东市| 屯门区| 邯郸市| 广西| 吴忠市| 巴楚县| 桑日县| 水城县| 鹤山市| 天峨县| 新密市| 呼和浩特市|