帖子

Memorial Edition

查看: 963|回复: 7

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

[复制链接]
mcbbsme_FaceMall

Lv.5 农夫

人气
55 点
金粒
1580 粒
宝石
36 颗
爱心
0 颗
钻石
17 颗
贡献
7 点

Java正版勋章Windows 10正版勋章

发表于 2025-2-4 01:38:40 来自手机 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 clok 于 2025-2-5 01:13 编辑

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

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


第一章 关于一个简单的击退原理介绍以及本篇文章的概要

读前须知

  1. 该教程相对于其他的击退,或许(此处表示也许)更加通俗易懂(仅仅对于而言)
  2. 初中生来了也能,但是你如果初中没有学习向量的话可以稍微了解一点,稍微一点就会
  3. 我代码写的很烂,别喷,能看懂就行,这里主要是计算
  4. 该教程也是看着MinestomPVP总结出来的
  5. 此处不包含任何MoJang代码,依赖与Minestom
  6. 教程很长,大多数都是问题的解答,有实力的可以只看前两章,后两章只是解答问题
  7. 本文章使用了AI帮我把我的话我的代码总结成md文档,本人表达能力有限。不得不说我觉得AI最好用的功能就是写wiki了,其他真的一般般
  8. 测试环境:
    • 测试核心:Minestom
    • 核心版本:9803f2bfe3
    • 游戏版本:1.21.3

核心概念

在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 runKnockback(执行一个简单的击退)

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

    //这是Attack的击退!具体什么是attack击退你看第二章就知道了
    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)
    }

0.2.1

这是Attack的击退的代码!具体什么是attack击退你看第二章就知道了,最好别跳着看,你可以先不知道,先往下看,这代码只是助于你理解的,所以叫attack击退

一、坐标系与基础参数

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 检查速度合成公式

第二章 深度解构Damage击退与Attack击退的协同机制——精确控制双重击退的叠加效应

提前预判你的疑问

啥是Damage击退啊啥是Attack击退啊
    Damage击退就是每次攻击触发的
    Attack击退就是蓄力接近满格+击退触发的额外击退,这额外击退是在damage击退的基础上追加的一个加速度向量罢了
哪Damage击退的代码跟Attack代码有什么区别?
实际上我上一章讲到了一点attack击退,就是

核心概念重塑

Damage击退与Attack击退的本质差异

特性 Damage击退 Attack击退(暴击击退)
触发条件 所有攻击必定触发 疾跑+蓄力满格时触发
力度基数 基础值0.4f 额外追加0.5f~1.0f
方向基准 攻击者与目标位置关系 攻击者面朝方向
优先级 底层基础 上层叠加

代码全景

DamageKnockback.kt

//CONFIG
private const val MIN_DIRECTION_THRESHOLD = 1.0E-4
private const val RANDOM_DIRECTION_FACTOR = 0.01
private const val STRENGTH = 0.4f

object DamageKnockback :
    IDamageKnockback {

    // [DAMAGE击退核心逻辑]
    override fun processKnockback(damage: Damage, target: LivingEntity, knockbackStrength: Int): Boolean {
        val attacker = damage.attacker ?: return false

        val (dx, dz) = attacker.calculateKnockbackDirectionTo(target.position)

        target.sendHurtAnimation(dx, dz)

        return executeKnockback(target, attacker = attacker, source = damage.source, type = Type.DAMAGE, strength = STRENGTH, dx = dx, dz = dz)
    }

    private fun Entity.calculateKnockbackDirectionTo(target: Pos): Pair<Double, Double> {
        var dx = this.position.x - target.x
        var dz = this.position.z - target.z

        /*
        * 当方向向量过小时生成随机方向
        * 所以你在攻击的时候,与defender贴的很近,可能就会出现一种奇妙的现象,就是这个defender飞到你了你后面,或者左边或者右边
        *
        * */
        if (dx * dx + dz * dz < MIN_DIRECTION_THRESHOLD) {
            val random = ThreadLocalRandom.current()
            dx = random.randomDirectionComponent()
            dz = random.randomDirectionComponent()
        }
        return dx to dz
    }

    private fun ThreadLocalRandom.randomDirectionComponent() =
        nextDouble(-1.0, 1.0) * RANDOM_DIRECTION_FACTOR

    private fun LivingEntity.sendHurtAnimation(dx: Double, dz: Double) {
        //这个东西完全自愿选择!删了的话也没问题,客户端自行处理的动画没有太大差异
        if (this is Player) {
            val hurtDirection = calculateHurtDirection(dx, dz)
            sendPacket(HitAnimationPacket(entityId, hurtDirection))
        }
    }

    private fun Player.calculateHurtDirection(dx: Double, dz: Double): Float {
        val attackAngle = Math.toDegrees(atan2(dz, dx))
        return (attackAngle - position.yaw).toFloat().normalizeAngle()
    }
}

AttackKnockback.kt

object AttackKnockback : IAttackKnockback {

    // [ATTACK击退应用层]
    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 {
                    //这afterMoveAttack()实际上是为了限制玩家的速度
                    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 基础向量

从攻击者到目标的向量:  

var dx = attacker.x - target.x  
var dz = attacker.z - target.z  
坐标差类型 物理意义
dx > 0 攻击者在目标东侧
dz < 0 攻击者在目标北侧

1.2 随机扰动触发条件

当向量长度平方小于阈值时触发随机方向:  

if (dx*dx + dz*dz < MIN_DIRECTION_THRESHOLD) { // 默认1.0E-4  
    // 生成随机方向分量  
}  

典型场景:  

  • 攻击者与目标坐标完全重合  
  • 两者距离小于0.01格(√(1E-4) = 0.01)  

举例子

玩家A和玩家B在游戏中决斗,两人同时发动攻击,结果坐标几乎完全重合,距离小于0.01格。此时,游戏的随机扰动机制触发: 
原本玩家A的剑应该击中玩家B,但因扰动,剑锋偏转,擦肩而过。玩家B抓住机会,反手一击,反败为胜。玩家A无奈道:“就差0.01格,代码不让我赢!”

二、随机方向生成

2.1 随机分量算法

fun ThreadLocalRandom.randomDirectionComponent() =  
    nextDouble(-1.0, 1.0) * RANDOM_DIRECTION_FACTOR // 默认0.01  

生成范围:-0.01 ~ +0.01  

2.2 随机方向意义

随机值 方向偏移 视觉效果
dx=0.01 向东轻微偏移 目标微微右弹
dz=-0.008 向南轻微偏移 目标微微前弹

三、受伤动画同步

3.1 动画包发送

target.sendPacket(HitAnimationPacket(entityId, hurtDirection))  

关键参数:  

  • entityId: 受击实体的唯一标识  
  • hurtDirection: 伤害方向角度(单位:度)  

3.2 角度计算流程

  1. 计算攻击向量与东轴的夹角:  
    val attackAngle = Math.toDegrees(atan2(dz, dx)) // 范围(-180°~180°)
  2. 转换为相对于玩家视角的角度:  
    (attackAngle - playerYaw).normalizeAngle()  

3.3 我猜你还会有疑问

为啥你都对它造成伤害了,哪不有受伤动画了吗,为啥还要发一次呀!

我在一开始查资料的时候没有注意,我看了NMS源码,它也没有再发包,但是自己复刻出来发现缺点味,打起来不舒服,这不舒服来自窗口的抖动

哪么到底是为什么呢!?
    欸欸欸这要解释的话太长了,我放第三章,额外章好好给你讲讲,感兴趣的可以直接看那里

四、配置参数详解

参数名称 默认值 作用域 调整建议
MIN_DIRECTION_THRESHOLD 1.0E-4 方向计算 保持默认
RANDOM_DIRECTION_FACTOR 0.01 随机扰动幅度 根据战斗风格调整(最好保持默认就行,你玩家挤在一起的机会很少)
Type.DAMAGE.strength 0.4f 基础击退力度 参考PVP平衡需求(其他需求可以看第一章第五节)

五、调试技巧

5.1 方向可视化

  1. 在攻击者与目标之间绘制粒子线  
  2. 当触发随机方向时显示特殊标记  

5.2 数据监控

// 调试输出示例
println("原始方向: dx=$dx, dz=$dz")  
if (isRandomized) println("随机方向: $randDx, $randDz")  

六、与Attack击退的对比

特性 Damage击退 Attack击退
方向基准 攻击者位置 攻击者面朝角度
垂直控制 无特殊处理 地面限制
适用场景 弹射物、环境伤害 近战攻击
方向随机性 近距离强制随机 可配置扰动

6.1 我猜你有疑问

  • 为什么attack击退的计算与damage击退的计算有这么大的差异!?

    一 Damage击退:位置差决定方向
        1 空想
            想象攻击者(A)和目标(B)在平面上:
                A坐标:(x₁, z₁)
                B坐标:(x₂, z₂)
            击退方向 = B到A的反方向
        2 计算
                dx = A的x坐标 - B的x坐标  
                dz = A的z坐标 - B的z坐标
            比如A在(5,5),B在(0,0),dx=5-0=5,dz=5-0=5
            反向处理的话直接(-dx, -dz)即可
        3. 为什么这样设计?
            符合直觉:就像你推别人,对方会朝你相反方向后退
            自动适应:无论从哪个方向攻击,方向自动计算
    二、Attack击退:面朝角度决定方向(第一章有详细的计算过程和详解)
        1. 角度系统速成(虽然已经说过无数遍了)
            0°:面朝正南
            90°:面朝西
            180°:面朝正北
            270°:面朝东
        2. 计算步骤
            把角度转为弧度:
                弧度 = 角度 × (π/180)  
                比如90° → 1.5708弧度
            用三角函数计算方向:
                dx = sin(弧度)  
                dz = -cos(弧度) 
    三、为什么两种方式不同?
        1. Damage击退的特点
            适合远程攻击:箭、火球等
            无需玩家控制:方向自动计算(方向其实就是那碰撞点决定的)
    
        2. Attack击退的特点
            适合近战操作:玩家可以主动控制方向
            战术性强:可以通过调整面朝角度精确击退
    
    四、实战验证方法
        1. Damage击退测试
            让朋友站在(0,0)
            你站在(5,0)攻击
            观察朋友被击退到(-5,0)左右的方向(一定不是-5,0,因为会有衰减的)
        2. Attack击退测试
            面朝正南(0°)攻击
            朋友会被击退到正北方向
            转身90°再攻击,观察方向变化
    
    你现在应该能理解:
        Damage击退像被球砸中,方向由碰撞点决定
        Attack击退像用棍子推人,方向由你拿棍子姿势控制

第三章 精准控制客户端表现的关键设计——sendHurtAnimation

前情概要

这章开始往后都是问题篇章了,这章的问题在第二章3.3中提到

一、动画触发的本质区别

1.1 服务端与客户端的职责分离

触发源 动画类型 控制权归属 必要性
伤害计算 实体抖动+红屏 客户端 自动触发(不可靠)
HitAnimationPacket 击退方向动画 服务端 必须手动控制
  • 客户端自动动画:当客户端收到伤害事件时自动播放基础受伤效果,但:
    • ❌ 不包含精确的击退方向信息
    • ❌ 不同客户端版本表现不一致
    • ❌ 无法与自定义击退逻辑同步

二、HitAnimationPacket 的核心作用

2.1 数据包结构解析()

HitAnimationPacket(
    int entityId,    // 受击实体ID
    float yaw  // 伤害方向角度(单位:度)
)

2.2 关键功能实现

参数 客户端行为
entityId 确定要播放动画的实体
direction 控制以下行为:<br>1. 受伤实体头部转向<br>2. 客户端预计算击退

三、两组动画实验得出真相

3.1 实际表现对比

调用情况 客户端视觉效果 网络流量分析
不发送数据包 仅有轻微抖动,无方向性动画 减少1个数据包(约20字节)
发送HitAnimationPacket 完整击退方向动画+实体头部转向 增加关键方向信息同步

3.2 代码层面验证

LivingEntity#damage() 源码中:

// Minestom核心类
public void damage(@NotNull Damage damage) {
    // 自动触发的客户端效果仅限于:
    // 1. 生命值变化
    // 2. 基础受伤音效
    // 3. 无方向性的实体抖动
    // 没有击退方向动画!
}

四、伟大的科学!

4.1 角度计算公式

fun calculateHurtDirection(dx: Double, dz: Double): Float {
    val attackAngle = Math.toDegrees(atan2(dz, dx)) // [-180°, 180°]
    val relativeAngle = attackAngle - position.yaw  // 转换为玩家视角坐标系
    return normalizeAngle(relativeAngle)            // 标准化到[-180°, 180°]
}

4.2 同步必要性演示

假设场景:

  • 攻击者坐标:(5, 0, 5)
  • 受击者坐标:(0, 0, 0)
  • 攻击者Yaw:45°

不发送数据包时:

客户端计算方向角度 = atan2(0,0) = 0° // 无效值
实际表现:随机方向抖动

发送数据包时:

服务端计算:
attackAngle = atan2(5,5) = 45°
relativeAngle = 45° - 45° = 0°
最终动画:正对攻击者的完美方向同步,抖动更加舒服了,使我达到高潮

5.2 流量消耗测试(理论)

战斗强度 每秒数据包增量 带宽消耗
单人PVP 2-5 packets/s <1 KB/s
50人团战 100 packets/s ≈20 KB/s

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

如果还有问题请加入我们

  • QQ:995070869

评分

参与人数 7人气 +27 金粒 +1635 贡献 +5 宝石 +36 收起 理由
mcbbsme_FaceMall
Anzide
+ 3 MCBBS有你更精彩~
WisW + 2 MCBBS有你更精彩~
喜塔腊尔晴 + 1 支持
Yeqi + 3 + 100 同学YYDS
ABlueCat + 15 + 1500 + 5 + 36 MCBBS有你更精彩~
wolski + 2 + 30 👍
PixelRPG + 1 + 5 MCBBS有你更精彩~

查看全部评分

Lv.3 挖沙工

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

Java正版勋章Windows 10正版勋章

发表于 2025-2-4 02:56:05 | 显示全部楼层
居然有minestom的教程 必须支持一波

评分

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

查看全部评分

回复

使用道具 举报

管埋员

三叉戟

人气
1563 点
金粒
9641 粒
宝石
58 颗
爱心
367 颗
钻石
1849 颗
贡献
52 点

石镐矿工勋章铁镐矿工勋章钻镐矿工勋章青苹果勋章红苹果勋章小麦种勋章苹果树勋章下界疣勋章论坛十周年纪念勋章面包勋章曲奇勋章荣誉版主勋章论坛元老勋章骨灰勋章Java正版勋章Android正版勋章Windows 10正版勋章金锭勋章金块勋章开发人员勋章新闻官勋章专业开发者勋章士兵勋章第8届创意赛参与奖优秀小组勋章-PluginsCDTribe

发表于 2025-2-4 17:45:51 | 显示全部楼层
非常好的教程,我还是通过这篇文章了解到的minestom服务端,这个服务端的介绍、应用和面向的编程目前在中文社区还处于蓝海状态,且该服务端的实现看起来比较有趣。个人认为值得继续挖掘,感谢分享!
另外,经过编程开发版版主讨论,决定授予精华1评级,希望您能继续这方面的研究并分享。
😭回来吧MCBBS😭
🌟我最骄傲的信仰🌟
⚡️历历在目的帖子⚡️
😭眼泪莫名在流淌😭
回复

使用道具 举报

实习版主

人气
563 点
金粒
6133 粒
宝石
6 颗
爱心
88 颗
钻石
885 颗
贡献
4 点

猪灵勋章Java正版勋章Windows 10正版勋章开发人员勋章石镐矿工勋章铁镐矿工勋章钻镐矿工勋章青苹果勋章小麦种勋章苹果树勋章

发表于 2025-2-6 14:37:15 | 显示全部楼层
同学yyds,赶紧发写好的核心,插件发到插件板块。

点评

爱你  详情 回复 发表于 2025-2-6 14:56
回复

使用道具 举报

mcbbsme_FaceMall

Lv.5 农夫

人气
55 点
金粒
1580 粒
宝石
36 颗
爱心
0 颗
钻石
17 颗
贡献
7 点

Java正版勋章Windows 10正版勋章

 楼主| 发表于 2025-2-6 14:56:41 | 显示全部楼层
Yeqi 发表于 2025-2-6 14:37
同学yyds,赶紧发写好的核心,插件发到插件板块。

爱你
我有五个人格,严重人格分裂患者
第一人格(主人格):喜欢唱跳rap打篮球
第二人格:喜欢扣丁(扣完就进入贤者状态的那种扣丁)
第三人个:喜欢扣丁(真扣丁)
第四人格:喜欢看战鹰抽象
第五人格:喜欢看永雏唐菲,还喜欢第五人格启动!
回复

使用道具 举报

Lv.7 猎手

我的,世界。

人气
47 点
金粒
1073 粒
宝石
0 颗
爱心
0 颗
钻石
427 颗
贡献
0 点

石镐矿工勋章铁镐矿工勋章骨灰勋章Java正版勋章Windows 10正版勋章

发表于 2025-2-6 15:37:39 | 显示全部楼层
大佬啊!
纯朴与真诚在任何时代总是合时宜的
回复

使用道具 举报

mcbbsme_FaceMall

Lv.9 牧场主

人气
666 点
金粒
91150 粒
宝石
11 颗
爱心
2 颗
钻石
2858 颗
贡献
12 点

论坛十周年纪念勋章论坛元老勋章骨灰勋章Java正版勋章Windows 10正版勋章金锭勋章联机爱好者勋章猪灵勋章石镐矿工勋章铁镐矿工勋章钻镐矿工勋章小麦种勋章苹果树勋章金块勋章

发表于 2025-2-10 01:18:05 | 显示全部楼层
本帖最后由 Anzide 于 2025-2-10 01:20 编辑

还是Minestom大佬
顺便说一嘴,我创了一个Minestom开发交流群,欢迎加入:438752464

Edit: 发现你已经加入啦
回复

使用道具 举报

Lv.3 挖沙工

人气
15 点
金粒
49 粒
宝石
0 颗
爱心
0 颗
钻石
2 颗
贡献
0 点

新人勋章

发表于 前天 22:37 | 显示全部楼层
嗯?
什么玩意在脑子里过了一下
回复

使用道具 举报

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

本版积分规则

他乡纵有当头月,不及故乡一盏灯。

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

GMT+8, 2025-2-21 23:00 , Processed in 0.128716 second(s), 55 queries , Redis On.

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

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

返回顶部