Skip to content

Commit ed3b25a

Browse files
entity-move: add [!ADVANCED] for 06 - FallingBlock and ItemEntity
Verified against D:\\guardian 1.20.1 Yarn: - FallingBlockEntity.tick(): GMD order (gravity -0.04, move, concrete raycast water, land/restore/drop, drag 0.98), 0t piston sand dupe prevention (isAir check) - ItemEntity.tick(): water/lava/air gravity branch, ground slipperiness drag (slipperiness*0.98), Y=-0.5 anti-bounce, merge logic (2gt when moving, 40gt when still, age 6000 despawn) - applyWaterBuoyancy(): X/Z *= 0.99, Y += 5E-4 if Y<0.06 - applyLavaBuoyancy(): X/Z *= 0.95, Y += 5E-4 if Y<0.06 - Ice road principle: blue ice slipperiness 0.989 → g=0.96922
1 parent 9a5532e commit ed3b25a

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

EntityMove/06-具体实体分析.zh.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,62 @@ Wiki以前上说落沙在很高的高空落下时可能会在触及地面前就
151151

152152
现存的末地门刷沙机的原理是在同一游戏刻内第3步移动的过程中使实体同时做到与末地门相交且在合适的地方着地,然后虽然末地门传送走了落沙实体(在新维度创建了新实体并把旧实体标记为移除),但落沙实体这一游戏刻的运算并不会因此终止,甚至还可以正常地复原,所以就复制了两份沙子------复原的一份和被传送的一份。不过,因为落沙实体无法通过下界传送门被传送,所以地狱门刷沙机并不可行。
153153

154+
## [!ADVANCED] 落沙与物品源码走读
155+
156+
### 下落的方块:GMD 顺序
157+
158+
```java
159+
// FallingBlockEntity.java (1.20.1 Yarn)
160+
public void tick() {
161+
if (this.block.isAir()) { this.discard(); return; }
162+
163+
this.timeFalling++;
164+
// 1. 重力:-0.04(与 TNT 相同)
165+
if (!this.hasNoGravity()) {
166+
this.setVelocity(this.getVelocity().add(0.0, -0.04, 0.0));
167+
}
168+
// 2. 移动
169+
this.move(MovementType.SELF, this.getVelocity());
170+
171+
if (!this.getWorld().isClient) {
172+
// 3. 混凝土粉末高速下落时 raycast 水源
173+
boolean isConcrete = this.block.getBlock() instanceof ConcretePowderBlock;
174+
boolean touchesWater = isConcrete && this.getWorld().getFluidState(blockPos).isIn(FluidTags.WATER);
175+
if (isConcrete && this.getVelocity().lengthSquared() > 1.0) {
176+
// 在移动路径上 raycast 检测水方块
177+
BlockHitResult hit = this.getWorld().raycast(...);
178+
if (hit is water) { blockPos = hit.getBlockPos(); touchesWater = true; }
179+
}
180+
181+
// 4. 着地或触水 → 复原或掉落
182+
if (this.isOnGround() || touchesWater) {
183+
this.setVelocity(this.getVelocity().multiply(0.7, -0.5, 0.7));
184+
if (!blockState.isOf(Blocks.MOVING_PISTON)) {
185+
if (canPlace) {
186+
this.getWorld().setBlockState(blockPos, this.block, ...); // 复原为方块
187+
this.discard();
188+
} else {
189+
this.discard();
190+
this.dropItem(block); // 掉落为物品
191+
}
192+
}
193+
}
194+
// 5. 超时掉落:落沙超过 100gt 且出界,或超过 600gt
195+
else if (timeFalling > 100 && outOfBounds || timeFalling > 600) {
196+
this.dropItem(block);
197+
this.discard();
198+
}
199+
}
200+
201+
// 6. 空气阻力:各轴 *= 0.98 — 原文提到的第 9 步
202+
this.setVelocity(this.getVelocity().multiply(0.98));
203+
}
204+
```
205+
206+
**GMD 验证**:重力(步骤 1)→ 移动(步骤 2)→ 复原/掉落逻辑(步骤 3-5)→ 阻力(步骤 6)。与 TNT 的 GMDF 相比,落沙多了第 3-5 步的着地处理块,且步骤 6 的阻力在着地处理之后——而这正是原文说的"第 7 步是多余的"的原因:着地后实体立即被 discard,multiply(0.7, -0.5, 0.7) 和 multiply(0.98) 都对已死的实体无影响。
207+
208+
**第 1 步的"是否被替换"检查**:原文推测此检查用于修复 0t 活塞刷沙。1.20.1 中确实保留了 `if (this.block.isAir()) { discard(); }`。如果落沙方块在计划刻创建后、实体运算前被活塞推走(方块变为空气),此检查直接废弃该实体。
209+
154210
## 物品
155211

156212
获取物品实体的途径有很多,常见的有:
@@ -219,6 +275,87 @@ Wiki以前上说落沙在很高的高空落下时可能会在触及地面前就
219275

220276
可能得问了,物品与流体到底啥关系,在这里差不多光说流体的影响了。实际上,物品的运动研究主要还是在流体和冰道运输以及物品收集,但冰道运输只是滑度和粘液块弹射的结合,没有特殊内容,而收集装置设计上还是靠实验比较好,真正的计算并无一般规律,在此从略。
221277

278+
## [!ADVANCED] 物品实体源码走读
279+
280+
### 物品的运动循环
281+
282+
```java
283+
// ItemEntity.java (1.20.1 Yarn)
284+
public void tick() {
285+
if (this.getStack().isEmpty()) { this.discard(); return; }
286+
super.tick(); // 调用 Entity.tick() → baseTick()
287+
288+
// 捡起延迟递减
289+
if (this.pickupDelay > 0 && this.pickupDelay != 32767) { this.pickupDelay--; }
290+
291+
this.prevX = this.getX(); ...
292+
Vec3d vec3d = this.getVelocity(); // 保存运动前速度
293+
float f = this.getStandingEyeHeight() - 0.11111111F;
294+
295+
// ===== 流体分支 =====
296+
if (this.isTouchingWater() && this.getFluidHeight(FluidTags.WATER) > f) {
297+
this.applyWaterBuoyancy(); // 水中浮力
298+
} else if (this.isInLava() && this.getFluidHeight(FluidTags.LAVA) > f) {
299+
this.applyLavaBuoyancy(); // 岩浆浮力
300+
} else if (!this.hasNoGravity()) {
301+
this.setVelocity(this.getVelocity().add(0.0, -0.04, 0.0)); // 空气重力
302+
}
303+
304+
// ===== 移动 + 阻力 =====
305+
if (!this.isOnGround() || this.getVelocity().horizontalLengthSquared() > 1.0E-5F
306+
|| (this.age + this.getId()) % 4 == 0) {
307+
this.move(MovementType.SELF, this.getVelocity());
308+
float g = 0.98F;
309+
if (this.isOnGround()) {
310+
// 着地时:阻力乘数 = 方块滑度 × 0.98
311+
g = this.getWorld().getBlockState(this.getVelocityAffectingPos())
312+
.getBlock().getSlipperiness() * 0.98F;
313+
}
314+
this.setVelocity(this.getVelocity().multiply(g, 0.98, g));
315+
// 着地且下落时:Y *= -0.5(防反弹)
316+
if (this.isOnGround() && this.getVelocity().y < 0.0) {
317+
this.setVelocity(this.getVelocity().multiply(1.0, -0.5, 1.0));
318+
}
319+
}
320+
321+
// ===== 物品合并 =====
322+
boolean moved = MathHelper.floor(this.prevX) != MathHelper.floor(this.getX()) || ...;
323+
int mergeInterval = moved ? 2 : 40; // 移动时每 2gt 检查一次合并
324+
if (this.age % mergeInterval == 0 && !this.getWorld().isClient && this.canMerge()) {
325+
this.tryMerge();
326+
}
327+
328+
// 物品寿命(6000 gt = 5 分钟)
329+
if (this.itemAge != -32768) { this.itemAge++; }
330+
// 流体状态更新(含流体加速)
331+
this.velocityDirty = this.velocityDirty | this.updateWaterState();
332+
if (!this.getWorld().isClient && this.itemAge >= 6000) { this.discard(); }
333+
}
334+
```
335+
336+
### 物品的流体浮力
337+
338+
```java
339+
// ItemEntity.java
340+
private void applyWaterBuoyancy() {
341+
Vec3d v = this.getVelocity();
342+
// 水中:X/Z *= 0.99,Y += 0.0005(仅当 Y < 0.06 时)
343+
this.setVelocity(v.x * 0.99F, v.y + (v.y < 0.06F ? 5.0E-4F : 0.0F), v.z * 0.99F);
344+
}
345+
346+
private void applyLavaBuoyancy() {
347+
Vec3d v = this.getVelocity();
348+
// 岩浆:X/Z *= 0.95,Y += 0.0005(仅当 Y < 0.06 时)
349+
this.setVelocity(v.x * 0.95F, v.y + (v.y < 0.06F ? 5.0E-4F : 0.0F), v.z * 0.95F);
350+
}
351+
```
352+
353+
**关键发现**:物品在水中同时受空气阻力和流体阻力——`updateWaterState()` 在末尾调用流体加速,而 `multiply(g, 0.98, g)` 在移动之后已经应用了空气阻力。这与原文"物品同时受到空气阻力和流体阻力"一致。
354+
355+
**冰道运输原理**:着地时 `g = getSlipperiness() * 0.98`。蓝冰滑度 `0.989``g = 0.989 * 0.98 = 0.96922`——这是物品在地面上的有效阻力乘数,远小于空气中的 `0.98`(水平方向),解释了冰道上物品能滑行极远的原因。
356+
357+
**物品合并的条件**:移动时每 2gt 尝试一次合并(`mergeInterval = moved ? 2 : 40`)。`canMerge()` 检查两个物品是否相同类型、都未满堆叠、年龄条件等。合并时取两者中较小的 `itemAge`,因此新物品的寿命从合并时重新计算。
358+
222359
## 经验球
223360

224361
获取经验球实体的方法主要有:

0 commit comments

Comments
 (0)