帖子

Memorial Edition

查看: 34|回复: 1

[其他编程作品] [Java核心开发]手把手使用Minestom带你解剖Minecraft的击退机制 ——实现精准可控的PVP击退效果

[复制链接]

Lv.3 挖沙工

人气
17 点
金粒
172 粒
宝石
0 颗
爱心
0 颗
钻石
8 颗
贡献
0 点
发表于 4 小时前 来自手机 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 clok 于 2025-2-4 01:40 编辑

手把手使用Minestom带你解剖Minecraft的击退机制

——实现精准可控的PVP击退效果


读前须知

  1. 该教程相对于其他的击退,或许(此处表示也许)更加通俗易懂(仅仅对于而言)
  2. 初中生来了也能,但是你如果初中没有学习向量的话可以稍微了解一点,稍微一点就会
  3. 我代码写的很烂,别喷,能看懂就行,这里主要是计算
  4. 该教程也是看着MinestomPVP总结出来的
  5. 此处不包含任何MoJang代码,依赖与Minestom

核心概念

在Minecraft中,击退(Knockback,简称KB)的本质是 通过方向向量和速度合成实现的物理效果 要实现优秀的PVP击退,需掌握以下核心要素:  

  1. 击退方向: 由攻击者的面朝角度(Yaw)决定  
  2. 击退力度: 由武器属性和状态效果调节  
  3. 速度合成: 结合目标当前速度和击退力  

零、代码展示

0.1 takeKnockback(对目标执行一个击退)


    @JvmStatic
    fun executeKnockback(
        target: LivingEntity,
        //source也许是一个空的
        source: Entity?,
        strength: Float,
        dx: Double,
        dz: Double,
        type: Type,
        attacker: Entity,
        verticalLimit: Double = 0.4
    ): Boolean {
        val event = EntityKnockbackEvent(target, source ?: attacker, type ,strength, verticalLimit).apply {
            EventDispatcher.call(this)
        }

        if (event.isCancelled) return false

        takeKnockback(target, event.strength, dx, dz, event.verticalLimit)
        return true
    }

    /*
    *空中连击为什么难?因为limit参数卡死了上升速度!
    *在20tick服务器,Y轴速度最大=0.4×20=8格/秒——
    *这就是你无法把对手打成卫星的根本原因!
    *
    * */
    @JvmStatic
    fun takeKnockback(entity: LivingEntity, str: Float, x: Double, z: Double, limit: Double = 0.4) {
        var strength = str
        if (strength > 0.0f) {
            strength *= ServerFlag.SERVER_TICKS_PER_SECOND.toFloat()
            //这是做了个归一化,如果不做归一化的话,可能会使击退数据出现异常,增加或降低
            val velocityModifier = Vec(x, z).normalize().mul(strength.toDouble())

            //在20tick服务器,Y轴速度最大=limit×20=8格/秒——
            val verticalLimit = limit * ServerFlag.SERVER_TICKS_PER_SECOND.toDouble()
            entity.velocity = Vec(
                entity.velocity.x() / 2.0 - velocityModifier.x(), if
                        (entity.isOnGround) min(verticalLimit, entity.velocity.y() / 2.0 + strength.toDouble())
                else entity.velocity.y(), entity.velocity.z() / 2.0 - velocityModifier.z()
            )
        }
    }
外部参数名称 类型 默认值 作用域 功能描述
entity: LivingEntity 一只富有活力实体 - 函数入参 被施加击退效果的目标实体(玩家/生物)
str: Float 浮点数 - 函数入参 基础击退强度(受武器、附魔、药水等影响)
x: Double 双精度浮点 - 函数入参 击退方向的 X 轴分量(东西方向)
z: Double 双精度浮点 - 函数入参 击退方向的 Z 轴分量(南北方向)
limit: Double 双精度浮点 0.4 函数入参 垂直速度上限(格/秒)
内部变量名称 类型 默认值 作用域 功能描述
strength 浮点数 - 函数内部变量 经过 Tick 率换算后的实际击退强度
velocityModifier Vec 向量 - 函数内部变量 归一化后的击退方向向量 × 强度
verticalLimit 双精度浮点 - 函数内部变量 根据服务器 Tick 率换算的垂直速度上限

0.2 attackKnockback(执行一个攻击的击退)

    private const val DEFAULT_KNOCKBACK_FACTOR = 0.5f
    private const val DEFAULT_VERTICAL_LIMIT = 0.4

    override fun processKnockback(
        attacker: Entity,
        target: LivingEntity,
        knockbackStrength: Int
    ): Boolean {
        if (knockbackStrength <= 0) return false

        return with(attacker.position) {
            val horizontalStrength = knockbackStrength * DEFAULT_KNOCKBACK_FACTOR
            val (dx, dz) = calculateAttackDirectionComponents(yaw)

            executeKnockback(
                target = target,
                source = attacker,
                strength = horizontalStrength, dx = dx, dz = dz, type = Type.ATTACK, attacker = attacker, DEFAULT_VERTICAL_LIMIT
            )
                .also {
                    success -> if (success && attacker is GamePlayer) attacker.afterMoveAttack()
                }

        }
    }

    private fun calculateAttackDirectionComponents(yaw: Float): Pair<Double, Double> {
        val radianYaw = Math.toRadians(yaw.toDouble())
        return sin(radianYaw) to -cos(radianYaw)
    }

一、坐标系与基础参数

1.1 世界坐标系

  • X轴:东(+) ↔ 西(-)  
  • Z轴:南(+) ↔ 北(-)  
  • Y轴:垂直方向(上+,下-)  

1.2 面朝角度(Yaw)

  • :正南(-Z方向)  
  • 90°:西(-X方向)  
  • 180°:正北(+Z方向)  
  • 270°:东(+X方向)  

二、击退方向计算

2.1 基础公式

击退方向由攻击者的Yaw角度通过三角函数计算:  

   //将角度转为弧度制
   val radianYaw = Math.toRadians(yaw.toDouble())
   //计算生成一个Pair<Double,Doblue>的一对数值
   return sin(radianYaw) to -cos(radianYaw)  

关于2.1

你或许会有的疑问:

  为什么dx不带负号而dz带了呢?:

     我们前面说过面朝的角度(Yaw)与方向的关系我们可以知道Yaw角度与方向的关系:
     Z轴和南北有关
     X轴与东西有关

     第一步:理解 Minecraft 的角度系统
        这与数学中的标准角度系统(0° 为东,逆时针旋转)不同,Minecraft 的 yaw 是 以正南为起点顺时针旋转
     可见我们需要调整三角函数的计算方式

     第二步:击退方向的反向逻辑
        当攻击者挥剑时,击退方向与攻击者的面朝方向相反(类似“向后推”)例如:
        攻击者面朝 正南(+Z) → 击退方向是 正北(-Z)
        攻击者面朝 东(+X) → 击退方向是 西(-X)
     可见为了反向,代码中需要将面朝方向的分量取反

     第三步:X 和 Z 分量的推导
        1. X 轴(东西方向)的分量:x = sin(yaw)
           在MC中,sin(θ) 对应 东西方向的分量
              当玩家面朝 东(yaw=270°) 时:sin(270°) = -1 → 击退方向为 西(-X)
              当玩家面朝 西(yaw=90°) 时:sin(90°) = 1 → 击退方向为 东(+X)
        2. Z轴(南北方向)的分量:z = -cos(yaw)
           cos(yaw) 原本对应 南北方向的分量,但需要反向
           当玩家面朝 南(yaw=0°) 时:cos(0°) = 1 → -cos(0°) = -1 → 击退方向为 北(-Z)
           当玩家面朝 北(yaw=180°) 时:cos(180°) = -1 → -cos(180°) = 1 → 击退方向为 南(+Z)
     第四步:举例子
        例1:攻击者面朝正南(yaw=0)
           x = sin(0°) = 0 → 无东西方向分量
           z = -cos(0°) = -1 → 击退方向为 北(-Z)
           ✅ 符合预期(面朝南,击退向北)
        例2:攻击者面朝东(yaw=270°)
           x = sin(270°) = -1 → 击退方向为 西(-X)
           z = -cos(270°) = 0 → 无南北方向分量
           ✅ 符合预期(面朝东,击退向西)
        例3:攻击者面朝西北(yaw=135°)
           x = sin(135°) ≈ 0.707 → 击退方向为 东南(+X)

           z = -cos(135°) ≈ 0.707 → 击退方向为 东南(+Z)
           ✅ 合成为 东南方向,与面朝西北相反

     你还可以这么想,通过诱导公式可以得出以下公式
        sin(-α) = -sinα
        cos(-α) = cosα
     这组诱导公式得到的sin都是相反的,但是cos不是,想要得到相反的直接加负号不就好了

2.2 方向验证表

面朝方向 Yaw dx dz 击退方向
正南 0.0 -1.0 正北
正东 270° -1.0 0.0 正西
东北 45° 0.707 0.707 西南

三、归一化处理

3.1 问题描述

原始方向向量的长度不固定(如东北方向长度为√2≈1.414),直接使用会导致:

  • 相同力度下,斜向击退距离比正方向远41%  

3.2 解决方案

将方向向量转换为单位向量(长度=1):
原代码(Kotlin):

   val velocityModifier = Vec(dx, dz).normalize().mul(strength.toDouble())

实际表达的意思(Java):

// 计算向量长度  
double length = Math.sqrt(dx * dx + dz * dz);  

// 归一化  
if (length > 0) {  
    dx /= length;  
    dz /= length;  
}  

3.3 效果对比

原始向量 归一化结果
(3, 4) (0.6, 0.8)
(1, 1) (0.707, 0.707)

四、速度合成算法

4.1 计算公式

原代码:

      // 水平速度
      val newVelX = entity.velocity.x() / 2.0 - velocityModifier.x()
      val newVelZ = entity.velocity.z() / 2.0 - velocityModifier.z()
      // 垂直速度  
      val newVelY = if (entity.isOnGround) min(verticalLimit, entity.velocity.y() / 2.0 + strength.toDouble()) else entity.velocity.y()
      entity.velocity = Vec(newVelX, newVelY, newVelZ)

4.2 参数说明

  • entity.velocity:目标当前速度矢量  
  • velocityModifier:处理后的速度矢量,实际上就是原速度矢量*strength
  • /2.0:模拟惯性衰减  

五、实现优秀PVP击退的配置指南

5.1 参数推荐值

战斗风格 strength 适用场景
竞技精准 0.6~0.8 1v1决斗
快速连击 0.4~0.6 连招Combo
爆发控制 1.0~1.2 群体PVP

5.2 进阶优化技巧

  1. 地面限制增强  

    // 增强地面单位的垂直击退  
    val newVelY = if (entity.isOnGround) min(verticalLimit + 0.2, entity.velocity.y() / 2.0 + strength.toDouble() * 1.2) else entity.velocity.y()
  2. 空中击退补偿  

    // 对空中单位施加20%额外水平击退  
    if (!entity.isOnGround) entity.velocity = Vec(newVelX * 1.2, newVelY, newVelZ * 1.2)
  3. 方向随机扰动(防预判,同时这也会为将要讲的DamageKnockback埋下伏笔)  

    // 添加±5°随机偏移  
    val randomOffset = ThreadLocalRandom.current().nextDouble(-5.0, 5.0)
    //这里的yaw是processKnockback里的那个calculateAttackDirectionComponents(yaw)里面的这个yaw
    val adjustedYaw = yaw + randomOffset
    //在processKnockback里的calculateAttackDirectionComponents(yaw)改成calculateAttackDirectionComponents(adjustedYaw)

六、调试与验证方法

6.1 调试工具

  1. F3调试界面:实时查看实体坐标和速度  
  2. Replay Mod:录制并回放击退轨迹  
  3. 自定义ActionBar:显示方向向量和力度数值  

6.2 验证流程

  1. 面朝正南攻击,确认目标向北移动(Z坐标递减)  
  2. 使用45°方向攻击,验证击退距离 = strength × √2  
  3. 连续攻击空中目标,检查垂直速度是否符合预期  

七、常见问题解决方案

问题现象 排查重点 解决方案
击退方向相反 检查dz是否使用-cos(yaw) 修正符号
斜向击退距离异常 确认是否执行归一化 添加归一化处理
空中单位无击退效果 检查onGround判断逻辑 移除不必要的条件限制
连击时速度指数增长 验证速度衰减是否使用/2.0 检查速度合成公式

通过以上步骤的系统化实施,你将能够精确控制Minecraft的击退机制,打造出既符合物理直觉又具备竞技深度的PVP体验

如果还有问题请加入我们

  • QQ:995070869

评分

参与人数 2人气 +3 金粒 +35 收起 理由
wolski + 2 + 30 👍
PixelRPG + 1 + 5 MCBBS有你更精彩~

查看全部评分

Lv.3 挖沙工

人气
16 点
金粒
5 粒
宝石
0 颗
爱心
0 颗
钻石
3 颗
贡献
0 点

新人勋章Java正版勋章Windows 10正版勋章

发表于 3 小时前 | 显示全部楼层
居然有minestom的教程 必须支持一波

评分

参与人数 1人气 +1 金粒 +5 收起 理由
clok + 1 + 5 赞一个!

查看全部评分

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

神即道,道即法,道法自然,如来。

Archiver|小黑屋| MCBBS纪念版 ( 新ICP备2024014954号|兵公网安备66010002000149号 )|隐私政策| 手机版

GMT+8, 2025-2-4 06:07 , Processed in 0.093897 second(s), 24 queries , Redis On.

"Minecraft"以及"我的世界"为美国微软公司的商标 本站与微软公司没有从属关系

© 2010-2025 MCBBS纪念版 版权所有 本站内原创内容版权属于其原创作者,除作者或版规特别声明外未经许可不得转载

返回顶部