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

128 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
front: https://nie.res.netease.com/r/pic/20210728/5507b669-4c6f-4958-b5d0-b8556ab4cfb5.png
hard: 进阶
time: 10分钟
---
# ModAPI代码进阶优化
> 本文档介绍了一些高级的ModAPI代码优化技巧帮助开发者们编写更高效的代码。
> 我们将结合一些开发情景,逐一讲解优化流程
## 使用实体Attr实现Molang同步
### 背景说明
在开发过程中假设我们需要给玩家添加创造飞行或鞘翅动画效果。但是翻找wiki却发现原版并没有提供相关的Molang所以我们需要自定义以下两个Molang变量
```
query.mod.ysm_is_create_flying # 是否正在创造飞行
query.mod.ysm_is_elytra_flying # 是否正在鞘翅飞行
```
### 实现方案对比
**不推荐的方案:**
通过tick函数持续获取玩家状态并与客户端通信来设置Molang值
这种方式会导致严重的性能开销,十分的不友好
**推荐的方案:**
结合以下两个关键功能实现:
1. [OnPlayerActionServerEvent](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/事件/玩家.html#onplayeractionserverevent) - 用于监听玩家动作状态
2. [实体自定义属性](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/接口/实体/索引.html?catalog=1#自定义属性) - 实现Molang值的自动同步
### 工作原理
众所周知,当使用服务端的`SetAttr`接口设置属性值后,系统会自动将这些值同步到客户端
在实体Attr文档中我们可以找到一个关键接口`RegisterUpdateFunc`,它用于注册属性值变化时的回调函数
这个机制就是实现实体Molang值自动同步的核心
### Attr同步优点
- 高性能、低功耗的实现方式
- 使用游戏原生发包的Attr通信比直接通信接口更快
- 自动处理可见渲染范围内的同步,无需手动监听`AddPlayerCreatedClientEvent`事件
- 适合在tick内进行多次操作统一更新数据
### 代码实现
##### 服务端代码
```python
# 服务端代码实现
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)
```
##### 客户端代码
```python
# 客户端代码实现
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)
# key为需要监听的attr名称,value为需要设置的对应Molang变量名称
queryDict = {
"playerIsFlying": "query.mod.ysm_is_flying",
"playerIsElytraFlying": "query.mod.ysm_is_elytra_flying"
}
# 根据queryDict自动配置监听,简化代码
def CreateAttrCallBack(bindQuery):
def _eventFuckCallBack(args):
ClientComp.CreateQueryVariable(args["entityId"]).Set(bindQuery, args["newValue"])
return _eventFuckCallBack
class PlayerActionClientSystem(ClientSystem):
def __init__(self, namespace, systemName):
ClientSystem.__init__(self, namespace, systemName)
# 注册本地玩家的属性值变化回调函数
for attr, query in queryDict.items():
ClientComp.CreateModAttr(playerId).RegisterUpdateFunc(attr, CreateAttrCallBack(query))
def OnAddPlayerAOIClient(self, args):
"""玩家加入游戏或进入视野时触发,注册属性值变化回调"""
pId = args["playerId"]
for attr, query in queryDict.items():
ClientComp.CreateModAttr(pId).RegisterUpdateFunc(attr, CreateAttrCallBack(query))
```
### 工作流程说明
1. 客户端为本地玩家和其他玩家注册属性值变化的回调函数
2. 服务端通过`OnPlayerActionServerEvent`事件监测玩家的飞行状态
3. 使用`SetAttr`设置属性值,自动同步到客户端
4. 客户端的回调函数被触发更新相应的Molang值
#### 其他性能优化教程,敬请期待...