V0.31.0.0 Release Note
注意事项
Devtools的实时日志功能关闭
由于devtools工具中打印的字符串log不会被回收,在长时间调试项目时,如果log打印过多会导致编辑器崩溃,所以该版本关闭了devtools的实时日志功能
对象管理器中的“优先加载”更名为“预加载”
为了更清晰地表达资源预加载的功能,对象管理器中原 “优先加载” 将更名为 “预加载资源”。
新增功能
一、编辑器交互及发版策略
[新增]网络资源库
如有任何网络资源库使用相关的问题反馈和建议欢迎直达网络资源库使用反馈
为了便于大家检索和预览资源,我们推出了【网络资源库】功能。可以在网络资源库功能界面中获取官方提供的资源包,并且能更方便的预览资源和搜索资源。
从资源库左下角的【网络资源库】按钮打开资源库界面。
主页
- 新增口袋方舟官方新闻界面
- 新增口袋方舟精选资源收藏夹列表
- 新增切换资源排序方式功能,能够以好评优先、最新上传、默认排序三种排序方式检索所有资源
资源库
新增筛选栏功能;优化资源搜索逻辑;
- 分类栏
- 单击分类可选中此分类进行筛选,单击下拉按钮可展开二级分类
搜索
网络资源库针对搜索逻辑进行了迭代优化,具体内容有:
搜索栏对输入内容进行匹配检索,默认展示与搜索内容关联程度最高的五个资源名称
搜索栏支持标签搜索、GUID搜索
搜索结果涉及的资源分类会高亮展示
- 筛选栏
支持搜索、分类、创作者id、标签四个维度的交叉筛选
- 同一维度筛选取并集,不同维度筛选取交集
TIP
筛选结果支持好评优先、最新上传、默认排序三种排序方式
资源缩略窗口功能
- 点击ID,可以查看该资源创作者上传的其他资源
- 点击GUID可以复制当前资源id,便于在脚本中使用
- 点击在场景中预览按钮,资源将生成在主视口内0点位置
- 点击收藏按钮,可将资源收藏至收藏夹
资源详情页
优化各类型资源预览视口,拖尾特效现在也可以直接在预览窗口中查看移动时的效果
我的收藏
本地资源库收藏功能模块移入网络资源库,支持收藏夹新增、复制、删除、分享
旧版本地资源库的收藏列表会同步至新版服务;本地资源库收藏功能将在后续版本开放
[新增]预制体批量导入功能,支持在导入界面中勾选可导入文件
[新增]预制体支持批量导出,支持在导出界面中勾选可导出文件
[优化] 修改脚本序列化处理规则避免脚本自定义属性丢失
在之前的版本中,经常会出现脚本自定义属性丢失的问题,这是因为原先加载脚本失败后如果触发序列化例如执行保存操作,那么由于脚本注册失败为空,导致序列化后自定义属性被覆盖后丢失。
针对该问题,我们在这个版本进行了修复:新版本在加载脚本过程中将监听报错状态进行标记,当触发序列化时会先读取当前数据,然后再进行写出,保证原level序列化的数据不被丢失。
二、性能相关优化
[优化] 打包去除SourceMap降低内存
原先包体含SourceMap用作保存TS、JS的映射关系。经过实测内存优化幅度根据项目不同大约减少15-30MB(项目代码越多 优化效果越明显)
注意
需要在031版本重新发布游戏才能享有本次优化
三、UI编辑器新增功能及API更新
[新增]UI控件-下拉菜单和勾选框
- 新增下拉菜单和勾选框两种UI控件,详情请查看产品手册:
[新增] 资源Icon支持高清分辨率
- 功能概述
针对资源库缩略图放大或者高精度的使用场景,提供setByAssetIcon
来使用不同分辨率的Icon图片。
以两个图片控件作为不同分辨率Icon的效果展示。在默认UI中新增两个图片控件:“Image_64”和“Image_128”,然后在DefaultUI脚本中onStart方法添加下列代码来申请不同分辨率的图片:
TypeScript
const image_64 = this.uiWidgetBase.findChildByPath('RootCanvas/Image_64') as Image
const image_128 = this.uiWidgetBase.findChildByPath('RootCanvas/Image_128') as Image
image_64.imageInfo.setByAssetIcon("20673", AssetIconSize.Icon_64px);
image_128.imageInfo.setByAssetIcon("20673", AssetIconSize.Icon_128px);
const image_64 = this.uiWidgetBase.findChildByPath('RootCanvas/Image_64') as Image
const image_128 = this.uiWidgetBase.findChildByPath('RootCanvas/Image_128') as Image
image_64.imageInfo.setByAssetIcon("20673", AssetIconSize.Icon_64px);
image_128.imageInfo.setByAssetIcon("20673", AssetIconSize.Icon_128px);
屏幕左侧 Icon 分辨率为64×64,右侧为128×128
- 后续计划
- 开放256和512像素的Icon图
- 提供更多下载和管理方法
- 将Icon纳入MW资源体系进行统一管理
注意
目前仅开放64和128像素Icon可选。此外由于Icon资源的特殊性,请求资源可能会有延迟。当图片已经存在于本地后,在游戏进程内会一直存在于内存中不会自动释放。
四、编辑器新增功能及API更新
[新增] RPC函数功能拓展
- 功能概述
- 新增RPC类型,目前支持组合如下:
RPC类型 | 客户端调用 | 服务端调用 | 使用示例 | 截图 |
---|---|---|---|---|
Client | 调用客户端执行 | 在指定客户端(player)执行 | // player参数指定客户端,客户端调用该参数无效 @RemoteFunction(Client) test_Client(player: Player) { console.log("test_Client"); } | |
Server | 服务端执行 | 服务端执行 | @RemoteFunction(Server) test_Server() { console.log("test_Server"); } | |
Client(可省略),Muticast | 调用客户端执行 | 所有活跃客户端执行 | *// 省略Client标签,功能与(Client,Multicast)一致* @RemoteFunction(Multicast) test_Multicast() { console.log("test_Multicast"); } | |
Server,Client | 调用客户端 + 服务端执行 | 服务端 + 指定客户端执行 | @RemoteFunction(Server, Client) test_Server_Client(*player*: Player) { console.log("test_Server_Client"); } | |
Server,Muticast | 调用客户端 + 服务端执行 | 服务端 + 所有活跃客户端执行 | @RemoteFunction(Server, Multicast) test_Server_Multicast() { console.log("test_Server_Multicast"); } |
- 端到端的RPC支持返回值:需注意仅异步函数支持
RPC类型 | 客户端调用 | 服务端调用 | 使用示例 | 截图 |
---|---|---|---|---|
Client,Result | 调用客户端执行 | 在指定客户端(player)执行 | @RemoteFunction(Client, Result) async test_Client_Result(*player*: Player): Promise<number> { console.log("test_Client_Result"); if(this.clientValue) { this.clientValue += 1; return this.clientValue; } else { return null; } } | |
Server,Result | 服务端执行 | 服务端执行 | @RemoteFunction(Server, Result) async test_Server_Result(): Promise<string> { console.log("test_Server_Result"); if(this.serverValue) { this.serverValue += "v"; return this.serverValue; } else { return null; } } |
- 静态函数支持RPC
一定程度上可以代替Event的功能。
TypeScript
@RemoteFunction(Server, Client)
static test_Server_Client(player: Player) {
console.log("test_Server_Client");
}
@RemoteFunction(Server, Multicast)
static test_Server_Multicast() {
console.log("test_Server_Multicast");
}
@RemoteFunction(Multicast)
static test_Multicast() {
console.log("test_Multicast");
}
@RemoteFunction(Server, Client)
static test_Server_Client(player: Player) {
console.log("test_Server_Client");
}
@RemoteFunction(Server, Multicast)
static test_Server_Multicast() {
console.log("test_Server_Multicast");
}
@RemoteFunction(Multicast)
static test_Multicast() {
console.log("test_Multicast");
}
- 后续计划
- 计划新增不可靠RPC满足项目优化需求,减少RPC堆栈溢出导致断线重连的情况。
- 改动Event广播机制,与RPC函数统一执行1.5s判定客户端挂起规则,减少广播造成的RPC堆栈溢出导致断线重连的情况。
TIP
- 目前使用的@RemoteFunction(Multicast)在收到客户端响应的前提下是可靠的
- 服务端如果1.5秒没收到客户端响应,之后的向对应客户端的广播类的rpc消息(multicast)就不会再发了
- 后续Event和RemoteFunction都会新增不可靠的使用方式
[新增] 异步获取子对象 & 子对象Transform不受父对象影响
- 功能概述
- 异步获取子对象
针对预制体嵌套结构+根节点挂载脚本,脚本内访问需要访问C端对象的使用场景,提供asyncGetChildByName
来异步获取下一层级的子对象,使脚本代码逻辑不受对象创建S->C同步时序的影响,可以在异步回调中顺利拿到已创建完成的子对象进行操作。通常在客户端的脚本内很有用。以下图预制体为例,在根节点(root)下挂载一个脚本,在onStart添加下列代码来实现打印预制体对象名称:
TypeScript
let go = this.gameObject;
if(SystemUtil.isClient()) {
let first = await go.asyncGetChildByName("正方体");
console.error("first " + first.name);
let second = await first.asyncGetChildByName("球体");
console.error("second " + second.name);
let third = await second.asyncGetChildByName("四棱锥");
console.error("third " + third.name);
}
let go = this.gameObject;
if(SystemUtil.isClient()) {
let first = await go.asyncGetChildByName("正方体");
console.error("first " + first.name);
let second = await first.asyncGetChildByName("球体");
console.error("second " + second.name);
let third = await second.asyncGetChildByName("四棱锥");
console.error("third " + third.name);
}
- 子对象Transform不受父对象影响
针对对象嵌套结构+子对象需要固定坐标/旋转/缩放的使用场景,提供setAbsolute
来独立指定子对象的坐标/旋转/缩放是相对与父对象,亦或者相对于世界。以摩天轮为例,在根节点(轮子)下挂载一个脚本,在onStart添加下列代码来实现机舱需要固定旋转的效果:
TypeScript
let roll = this.gameObject;
let children = roll.getChildren();
for (const child of children) {
child.setAbsolute(false, true, false);
}
let roll = this.gameObject;
let children = roll.getChildren();
for (const child of children) {
child.setAbsolute(false, true, false);
}
机舱未固定旋转 | 机舱固定旋转 |
- 后续计划
进一步完善setAbsolute
的方案:
- gameObject新增3个boolean属性:独立控制对象坐标/旋转/缩放的相对目标。
- 支持属性同步,服务端调用,广播生效
- 暴露至属性面板,可以直接在属性面板中做调整
如果对父子对象操作有进一步需求可以考虑:
- 增加父子对象变化委托
- 增加接口获取父子关系的层级
- 增加更多按各种条件查询/管理子对象接口
TIP
对于动态生成的预制体中的子对象setAbsolute后,子对象的相对目标会变为世界,也就是子对象会相对世界应用localTransfom。如果在动态生成预制体后,修改了预制体transfom,此时再对子对象应用setAbsolute()
,子对象的worldTransform会变化,变为其localTransfom。
- 由于目前
setAbsolute()
接口相当于将原生接口暴露出来,所以目前的预期效果较为基础,后续有使用上的建议欢迎反馈。 - 如果想
setAbsolute()
后,子对象的worldTransform不变,可以在setAbsolute()
之前,获取一下其worldTransform,在setAbsolute()
,再将其设置回去。
此接口仅调用端生效,不带同步功能。
[新增] 新增背包功能
编辑器内置了背包系统,可以使用【BagModule】开启背包功能,便于大家实现游戏中背包的存取,排序,搜索等功能。
详情请查看:背包功能| 产品手册 (ark.online)
[优化] 画质分级阴影优化
- 之前阴影效果比较固定,现在会根据画质分级调整游戏中的阴影效果,画质越高,阴影效果越好。
五、游戏功能对象新增功能及API更新
[新增]冲量对象属性面板中添加[启用]属性,可以设置在运行时是否自动启用冲量
TIP
纯服务端的冲量对象只对双端npc起作用,对玩家不起作用。
[新增] 角色头部跟随功能
- 提供了头部跟随的API,支持让角色头部跟随某个物体移动、世界坐标或摄像机进行旋转,注意角色头部在跟随物体旋转时,如果超过180度,将会重置。
- API:
函数名称 | 用法 | 说明 | 使用域 |
---|---|---|---|
头部跟随 | headFollow(target: mw.Vector | mw.GameObject | null): void; | 头部跟随(坐标/物体/空)跟随物体时,如果物体在移动,会实时跟随 | Client only |
取消头部跟随 | cancelHeadFollow(): void; | 取消头部跟随,并将头旋转进行重置 | Client only |
- 示例代码:
TypeScript
if(SystemUtil.isClient()) {
//声明摄像机
let camera = Camera.currentCamera
//获取玩家角色
let chara = Player.localPlayer.character
//按下“1”键触发以下逻辑
InputUtil.onKeyDown(Keys.One, () => {
//角色头部跟随摄像机
chara.headFollow(camera);
})
//按下“2”键触发以下逻辑
InputUtil.onKeyDown(Keys.Two, () => {
//取消头部跟随
chara.cancelHeadFollow();
})
}
if(SystemUtil.isClient()) {
//声明摄像机
let camera = Camera.currentCamera
//获取玩家角色
let chara = Player.localPlayer.character
//按下“1”键触发以下逻辑
InputUtil.onKeyDown(Keys.One, () => {
//角色头部跟随摄像机
chara.headFollow(camera);
})
//按下“2”键触发以下逻辑
InputUtil.onKeyDown(Keys.Two, () => {
//取消头部跟随
chara.cancelHeadFollow();
})
}
[新增] IK逻辑对象
我们新增了IK的逻辑对象,针对脚部和手部效果进行了优化,防止角色过度穿模的现象。
脚部IK可以优化角色在复杂地形上的站立效果。
手部IK可以优化角色拿枪支、武器等穿模效果,进一步提高手部交互位置的精准性。
详细功能请见产品手册:IK锚点 | 产品手册 (ark.online)
TIP
- IK关节朝向功能具体效果目前需要手动调整,后续会做可视化效果,便于大家调试
- IK对象目前未开放设置“锚点类型”的接口,使用时建议和对应物体组合成预制体生成使用。
[新增] 插槽功能支持动态骨骼挂件
角色插槽可以支持插入动态骨骼挂件和带动画的挂件,可以实现在角色身上放置会动的尾巴、会动的耳朵、飘带、翅膀等挂件。
[优化] 特效循环处理方案
- 特效循环问题一直争议比较大,本次我们特效面板上取消了是否循环的功能,根据特效本身的循环特性,自动划分为循环特效和不循环特效。
- 不循环特效:我们保留了【循环次数】的属性,确保大家可以使用循环次数去修改不循环特效的播放次数,在特效播放到指定次数后停止播放特效。如果循环次数为0,则变为一直循环播放的特效。
- 循环特效:循环特效由于本身就是一直循环的特效,并没有次数概念,所以我们新增暴露了【持续时间】的属性,可以使用持续时间,调整循环特效的播放时长,并在指定时间内停止播放特效。如果持续时间为0,则变为一直循环播放的特效。
- API:
属性名称 | 英文名称 | 类型 | 默认值 | 取值范围 | 说明 | 读写 |
---|---|---|---|---|---|---|
持续时间 | duration | number | 0 | - | 循环特效到持续时间后停止播放 | Write/Read |
TIP
可以在代码中用effect.loop()的返回值判断effect是否为循环特效