Files
netease-bedrock-wiki/mcguide/20-玩法开发/18-性能优化/代码进阶优化.md
2025-07-31 20:38:57 +09:00

5.2 KiB
Raw Blame History

front, hard, time
front hard time
https://nie.res.netease.com/r/pic/20210728/5507b669-4c6f-4958-b5d0-b8556ab4cfb5.png 进阶 10分钟

ModAPI代码进阶优化

本文档介绍了一些高级的ModAPI代码优化技巧帮助开发者们编写更高效的代码。 我们将结合一些开发情景,逐一讲解优化流程

使用实体Attr实现Molang同步

背景说明

在开发过程中假设我们需要给玩家添加创造飞行或鞘翅动画效果。但是翻找wiki却发现原版并没有提供相关的Molang所以我们需要自定义以下两个Molang变量

query.mod.ysm_is_create_flying      # 是否正在创造飞行
query.mod.ysm_is_elytra_flying      # 是否正在鞘翅飞行

实现方案对比

不推荐的方案:

通过tick函数持续获取玩家状态并与客户端通信来设置Molang值 这种方式会导致严重的性能开销,十分的不友好

推荐的方案:

结合以下两个关键功能实现:

  1. OnPlayerActionServerEvent - 用于监听玩家动作状态
  2. 实体自定义属性 - 实现Molang值的自动同步

工作原理

众所周知,当使用服务端的SetAttr接口设置属性值后,系统会自动将这些值同步到客户端 在实体Attr文档中我们可以找到一个关键接口RegisterUpdateFunc,它用于注册属性值变化时的回调函数 这个机制就是实现实体Molang值自动同步的核心

Attr同步优点

  • 高性能、低功耗的实现方式
  • 使用游戏原生发包的Attr通信比直接通信接口更快
  • 自动处理可见渲染范围内的同步,无需手动监听AddPlayerCreatedClientEvent事件
  • 适合在tick内进行多次操作统一更新数据

代码实现

服务端代码
# 服务端代码实现
ServerComp = serverApi.GetEngineCompFactory()

class PlayerActionServerSystem(ServerSystem):
    def __init__(self, namespace, systemName):
        ServerSystem.__init__(self, namespace, systemName)

    def OnPlayerActionServerEvent(self, args):
        # 玩家动作事件,当玩家开始/停止某些动作时触发该事件
        playerId = args["playerId"]
        actionType = args["actionType"]
        modAttr = ServerComp.CreateModAttr(playerId)
        
        # 使用鞘翅飞行/创造飞行的动作枚举值为(15,16,34,35)
        if actionType == 34:
            # 开始创造飞行
            modAttr.SetAttr("playerIsCreateFlying", 1)
        elif actionType == 35:
            # 停止创造飞行
            modAttr.SetAttr("playerIsCreateFlying", 0)
        elif actionType == 15:
            # 开始鞘翅飞行
            modAttr.SetAttr("playerIsElytraFlying", 1)
        elif actionType == 16:
            # 停止鞘翅飞行
            modAttr.SetAttr("playerIsElytraFlying", 0)
客户端代码
# 客户端代码实现
ClientComp = clientApi.GetEngineCompFactory()
levelId = clientApi.GetLevelId()
playerId = clientApi.GetLocalPlayerId()

# 注册自定义Molang变量
ClientComp.CreateQueryVariable(levelId).Register("query.mod.ysm_is_create_flying", 0)
ClientComp.CreateQueryVariable(levelId).Register("query.mod.ysm_is_elytra_flying", 0)

class PlayerActionClientSystem(ClientSystem):
    def __init__(self, namespace, systemName):
        ClientSystem.__init__(self, namespace, systemName)
        # 注册本地玩家的属性值变化回调函数
        modAttr = ClientComp.CreateModAttr(playerId)
        modAttr.RegisterUpdateFunc("playerIsCreateFlying", self.PlayerCreateFlyStateChanged)
        modAttr.RegisterUpdateFunc("playerIsElytraFlying", self.PlayerElytraFlyStateChanged)
    
    def OnAddPlayerAOIClient(self, args):
        """玩家加入游戏或进入视野时触发,注册属性值变化回调"""
        pId = args["playerId"]
        modAttr = ClientComp.CreateModAttr(pId)
        modAttr.RegisterUpdateFunc("playerIsCreateFlying", self.PlayerCreateFlyStateChanged)
        modAttr.RegisterUpdateFunc("playerIsElytraFlying", self.PlayerElytraFlyStateChanged)
    
    def PlayerCreateFlyStateChanged(self, args):
        """创造飞行状态变化回调"""
        molangVar = args["newValue"]  # 属性变化后的值
        pId = args["entityId"]        # 属性变化对应的实体Id
        ClientComp.CreateQueryVariable(pId).Set("query.mod.ysm_is_create_flying", molangVar)

    def PlayerElytraFlyStateChanged(self, args):
        """鞘翅飞行状态变化回调"""
        molangVar = args["newValue"]  # 属性变化后的值
        pId = args["entityId"]        # 属性变化对应的实体Id
        ClientComp.CreateQueryVariable(pId).Set("query.mod.ysm_is_elytra_flying", molangVar)

工作流程说明

  1. 客户端为本地玩家和其他玩家注册属性值变化的回调函数
  2. 服务端通过OnPlayerActionServerEvent事件监测玩家的飞行状态
  3. 使用SetAttr设置属性值,自动同步到客户端
  4. 客户端的回调函数被触发更新相应的Molang值

其他性能优化教程,敬请期待...