[3.8.4 升级必读]:Node 的一些 set 接口行为的修正

刚用了3.4.8,被这个设定震惊到了。

这样写代码居然无法刷新渲染
image

必须这样写才行
image

老项目升级到3.8.4基本失败

4赞

参考下unity lua?

    local MoveObject = {}
    
    -- 移动速度
    MoveObject.moveSpeed = 5
    
    function MoveObject:Start()
    -- 获取物体的Transform组件
    self.transform = self.gameObject.transform
    end
    
    function MoveObject:Update()
    -- 获取水平输入(A/D或左/右箭头键)
        local moveInput = Input.GetAxis("Horizontal")
    
    -- 更新位置
    local newPosition = self.transform.position
    newPosition.x = newPosition.x + moveInput * self.moveSpeed * Time.deltaTime
    self.transform.position = newPosition
    end
    
    return MoveObject

个人觉得如果相同坐标有性能问题先自己业务逻辑加判断,再查找底层问题。引擎组建议可参考下unity或虚幻代码,个人觉得引擎代码便捷性统一性优雅最优先 ,直接关系到新人老人是否愿意用cocos(既然unity没这个问题肯定哪里设计有问题或者不是个问题) :joy: 你看向僵尸开炮卡成那个吊吧样照样畅销榜前10。(当然找到原因优化是必须滴)

5赞

所以最好还是不要升级 就这个地方已经让我接受不了了 用得最多的地方就跑去改 要是大家能接受就没有那么多人吐槽了

2赞

我们内部认真讨论了。之前没想到竟然有这么多项目是这样的错误用法,为了兼容性,也只能把错误当成一个特性来对待了。我们打算在 3.8.5 改回和 3.8.3 一样的处理方式。

抱歉给大家带来了不便。

5赞

真的可以单独考虑加修改x,y,z,w,h的,大部分情况下修改的只是单个值,每次改属性值有种本能的恐惧感
然后我想表达下,官方鼓励的是private _worldPos = v3(); 或者 var pos=v3(),申请一个私有变量,为啥大家没有遵循,全局变量这类或者文件闭包内的游离变量在真实的工程项目中是避免/禁止选项,或者审核的时候代码就直接被打回了,还有就是这种方式确实会增加额外的代码维护,肯定是怎么简单怎么来,只有性能上扛不住了才会在某些地方去做特定的优化,个人感觉接口一致性优于性能

1赞

其实这个行为从理解上才是对的吧,我一直都是用的setPosition :grimacing:

1赞

是之前很多人直接修改node.position而这个其实是readonly的,个人觉得如果对性能真的有影响改就改了吧

1赞

有个疑问,既然node.position是readonly的,为什么可以直接对node.position进行赋值而不会报错呢,难道这个readonly只是声明下,并没有实际的约束力吗
image

1赞

设置节点位置,旋转,缩放的接口, 是游戏引擎的最基础的接口. 这个应该是在设计初期就设计好的.
用到的是基本的数据结构: Vector, Quat

vector: {x, y, z}, quat: {x, y, z, w}

第一种设置方法:

node.position = vector

第二种设置方法

node.position.set(vector)
// or
node.position.copy(vector)

第三种设置方法

node.position.x = number
node.position.y = number
node.position.z = number

第四种设置方法

node.setPosition(vector)

以上四种方法, 应该都可以正常的更新物体的位置, 不然就会造成混乱.

看上面的引擎开发人员说, 现在用的是 setter 的方式去做检查数据是否更新.
可以参考一下threejs中的实现. threejs中, 上面四种方式, 都是可以正常的修改物体的位置的.

1赞

有的人会用 as 大法,as Vec3 就可以了。

1赞

作为一个typescript初学者,好像理解了:
position是Node类的一个访问器,并不是Node类的readonly属性,因为position具有set方法,那它就是可以被赋值的,提出此问题的时候我以为它是readonly属性;
另外,position返回的是一个Readonly<math.Vec3>,其中ReadOnly是一个类型工具,它表示T的所有属性都只是可读的,所以不能对Vec3的x,y,z属性进行赋值,但是开发者可以用position as Vec3绕过属性只读的限制对x,y,z进行赋值操作,这样也违背了引擎设计的初衷。
这样理解应该没错吧 :sweat_smile:

2赞

是的,Readonly 的 set 方法可以被调用,ts 的 Readonly 只包装了 Vec3 内部的 x, y, z ,只保证了不能直接设置 x, y, z,但是无法杜绝其 set 方法间接地对 x, y, z 操作。

1赞

js就是这样 规范了也等于0,反正随便绕过。

1赞

直接来来一句//@ts-ignore 什么都不报了 :upside_down_face:

1赞

我提个方案啊,就拿position举例:


自始至终不需要position这个东西。
设置就是this.node.x = 50;
获取就是let x = this.node.x;
要emit事件就this.node.setPosition(50,100,0);
当然,对flag的统一处理要挪到每帧最后,避免重复计算
既然技术上无法超越U3D,就在使用体验上做到极致。
做一个最简洁,最容易上手的引擎。

2赞

修改 x,y,z ,其实也是 pos 变化了,为什么 x, y, z 单独改就不触发 emit 呢?
如果使用这个规则,那只能通过文档来告知了,那开发者如果有监听 POSITION_CHANGED 的话,会出现这样的吐槽:【3.x.x 引擎重大 BUG】通过 node.x 修改了坐标,居然不会触发 POSITION_CHANGED 的事件,而 2.x 并没有这个大 BUG

如果要加 x, y, z,那么就保持跟 2.x 一致的体验会比较好:

不要让开发者多添加思考负担了。

2赞

主要是考虑到,大部分设置x,y,z是不需要emit事件的,每个都emit属实有点多余

我们把是否要emit的选择权交给用户,文档告知一下就好了,有变更肯定要告知的

这样设计比2.x,比U3D都更加人性化

2赞

今天尝试把旧项目升级到3.8.4,整个项目动弹不得,要每个脚本每处position都处理一遍,根本搞不过来,还有个无解的问题,控制摄像机移动就算用你们推荐的这个新办法都无法刷新渲染。


截图里声明了一个全局变量targetPos,然后在update里先修改targetPos,再把targetPos赋值给摄像机的positon用于移动,根本移动不了。你们应该没有测过摄像机移动的bug

需要对摄像机先隐藏再显示一次才能刷新。

3.8.5回复回原来的设定算英明的,先留住用户再说,你们又不优化这点效率根本不影响什么,线上这么多3.8.3的游戏效率都没问题。

1赞

可以提供类似这样的接口,就不用clone坐标了,也不会造成gc。


为了方便开发,我都是自己封装底层组件,用于方便修改坐标,大小

3赞

setPosition

1赞