Unreal开发问题总结(9,10月)

1. ShowHiddenPropertiesWhilePlaying
在Actor的detail面板中可以勾选ShowHiddenPropertiesWhilePlaying来看到更多运行时的Debug信息,但打开后在运行时点选查看Actor有可能会导致使用时引擎直接卡死崩溃(除了自己点选会崩溃,使用AI Debugger之类的查看Actor的Debug工具也都会崩溃)
在这里插入图片描述

2. 通过编辑器调用函数修改Actor Property
通过编辑器调用的函数(比如某个编辑器按钮)修改场景中Actor的Property时要手动调用Actor::MarkPackageDirty,不然修改后不会自动保存也无法手动保存,reload场景修改就会丢失
3. WidgetComponent材质
通过Widgetcomponent创建的3D Widget是渲染在后处理阶段的,所以即便是Opaque+Unlit也会和场景中已渲染物体的颜色做Blend,有时会导致无论怎么调整颜色都过暗或者过亮,可以通过在材质中乘一个系数来做调整(让输出颜色值大于1)
4. XXX_API宏的作用
一个模块中的类想要能够被其他模块引用就必须加上该模块的API宏(XXX_API),否则调用时就会报无法解析的外部符号,即便已经在build.cs中添加了对该模块的引用。这不仅适用于UE的类型,像Protobuf生成的协议头文件中的消息类如果放在插件中也要手动加上模块的API宏才能被模块外使用
5. GC问题
在任何时候使用UObject裸指针都要注意GC问题,即便是在TArray或TMap中使用也需要加上UPROPERTY才能防止被GC(尤其是注意这种间接引用的情况,编辑器不会提醒你没有加UPROPERTY)。
同时GC导致的bug一般有两个特点,一是调试会发现对象属性乱码和奇怪的值;二是触发bug的场景没有业务逻辑上的特点,但总是在差不多同一时间触发,并且如果进行断点调试或者其他影响了时间的操作会影响触发bug的节点
Unreal的自动GC间隔一般都很长,可能达到10s,所以如果希望用引擎自动GC的方式回收某个对象,应当确保取消他监听的事件等,让他在等待GC的时间内不会被调用到
6. UMG TextWrap
TextBlock的TextWrap功能有两个WrappingPolicy,Default是只会在空格处Wrap,这种适用与英文但中文句子中一般一个空格都没有;中文的话需要设置为Per-Character Wrap,就能在任意字符处Wrap了。
AutoWrap会根据父级限定的空间来做Wrap,如果想在指定长度Wrap的话就只设置WrapTextAt而不要勾选AutoWrap
7. Unreal项目打包后的路径问题
运行时产生的中间文件(json自定义数据等)应当保存在Saved文件夹下
需要打包的非asset文件需要存放在Content目录内(Plugin内是不行的),并且在Project Setting内将对应目录添加至Additional Copy Path
代码中使用FPaths::ProjectDir、ProjectContentDir、SavedDir获取路径都会根据平台自动调整,不需要手动区别编辑器和打包状态
安卓完整路径内没有:/,所以如果通过这个判断某个字符串是否是完整路径那么打包后就会有问题
通过WITH_EDITOR宏判断是否是打包状态,具体哪种打包可以通过UE_BUILD_DEBUG/DEVELOPMENT/SHIPPING判断
8. 动画蓝图复用问题
动画蓝图的AnimGraph是无法通过继承复用的,并且如果一个动画蓝图继承了另一个动画蓝图,那他将没有自己的AnimGraph,只能通过AssetOverride来替换父动画蓝图AnimGraph的Sequence、Montage和BlendSpace。继承C++ AnimInstance父类是没关系的,因为C++ AnimInstance类只影响Event Graph
想要复用某个动画蓝图可以通过LinkedAnimGraph节点,貌似无法只复用某个AnimationLayer
9. 添加自定义Project Setting类型
如果是在插件模块中添加ProjectSetting可以创建一个自己的Setting类(继承UObject),除了为类和变量加上config宏外,需要注意必须手动在ISettingModule中RegisterSetting才能在ProjectSetting中创建对应的目录,可以在对应模块启动和卸载时执行RegisterSetting和UnregisterSetting,同时所有的注册代码以及对ISettingModule头文件的引用都要加WITH_EDITOR,否则打包会失败
如果是直接在主模块中添加ProjectSetting的话,可以创建一个继承自UDeveloperSetting的类型,通过重载函数来设置目录名称等信息。这个本质上和上一种方法是一样的,只是UDeveloperSetting会随着主模块的加载和卸载自动执行注册
10. 使用DeltaTime计算下一次执行的注意事项
假设某个函数中的代码需要以30FPS执行,则执行间隔为0.033s,如果用下面这种写法

	NextTick -= DeltaSeconds;
	if(NextTick > 0)
	{
		return;
	}
	NextTick = 1 / 30;
    ...

则无法得到正确的结果,因为实际帧率可能接近30FPS,所以单纯的把NextTick负的部分砍掉误差是不可接受的(比如有可能砍掉0.03),只有执行间隔远大于Deltatime比如1s以上才可以这样写
正确的写法应该是下面这样

	NextTick -= DeltaSeconds;
	if(NextTick > 0)
	{
		return;
	}
	NextTick += 1 / 30;
    ...

但这样写的话需要注意DeltaSecond大于1/30的情况,这会导致每帧都执行,当然对于本场景来说这是合理的;或者在某一次异常大的DeltaSeconds后会导致之后若干帧一直在追赶执行,考虑是否需要对这种情况做特殊处理
11. Unreal音频播放帧率
Unreal中音频的播放是在单独的线程上进行的,所以播放时长可以直接通过SoundBase->Duration获取,是固定的与帧率无关
12. Protobuf使用repeated字段储存大数组的问题
Protobuf中需要使用add_x()来为repeated字段添加项,这其中涉及到类似vector的resize机制,初始开辟大小为4,之后每次满了后double扩容,如果已经提前知道了总的大小可以通过以下写法来避免resize(c++中写法,python好像不可以)

Message.mutable_x()->Reserve(num)

同时对于string和message类型,每次add会new内存然后把指针存在数组中,而其他类型则是直接写值到数组中;我们都知道频繁的申请内存会严重影响性能,所以对于大数组的message或string类型可以考虑拆成其他类型代替,比如将vector message拆成单独的float

message Vector3{
    float x = 1;
    float y = 2;
    float z = 3;
}
message Transform{
    Vector3 location = 1;
    Vector3 rotation = 2;
    Vector3 scale = 3;
}
message Data {
    repeated Transform transforms = 1;
}

上面的这种消息嵌套在大数组中会严重影响性能,改写成这样可以显著降低时间消耗

message Data {
    repeated float transforms = 1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值