|
这系列文章记录了我平常实战中对于UE4引擎、开发工具、代码中遇到的问题的解决方法、遇到的坑和使用上的一些技巧,类似于学生时代的笔记本和错题集。每条单独拿出来都不足以支撑一篇博客的量,所以把这些零碎的经验汇总成一篇篇文章。计划出成一个系列,这一篇总结了今年上半年来的工作经验。
Lua使用的是SLua,UE4版本4.26。
<hr/>Lua
- os.time() 当前时间戳。
- os.time({ year = 2021, month = 7, day = 8, hour = 19, min = 0, sec = 0 }) 自定义时间戳。
- os.difftime(time1, time2) time1 - time2 计算时间戳差距,返回值是秒。
- 把秒数转化为类似&#34;09:11:22&#34;的时间字符串的优雅写法。
- math.modf 返回整数和小数部分,有两个返回值,这里只拿第一个
- string.format(&#34;%02d&#34;,xxx) 不足两位的整数补足0。%d表示数值变量,%s表示字符串变量
function GetTimeText(remainSec)
local secsOneDay = 24 * 60 * 60
local secsOneHour = 60 * 60
local remainDay = math.modf(remainSec / secsOneDay)
local remainHour = math.modf((remainSec % secsOneDay) / secsOneHour)
local remainMin = math.modf((remainSec - remainDay * secsOneDay - remainHour * secsOneHour) / 60)
local remainSec = remainSec - remainDay * secsOneDay - remainHour * secsOneHour - remainMin * 60
local timeText = string.format(&#34;%s:%s:%s&#34;,string.format(&#34;%02d&#34;, remainHour),string.format(&#34;%02d&#34;, remainMin),string.format(&#34;%02d&#34;, remainSec))
return timeText
end
- lua 排序table.sort()的注意事项:两个值相等时必须返回false。table.sort实现原理是快排,如果返回true会导致不必要的交换。
local array = {9,15,9,222,10}
table.sort(array, function(a, b)
return a < b
end)
- umg的SetVisiblity封装,避免每次都去c++里复制粘贴,自己拼还容易拼错。(Hidden用到的情况非常少所以没有加)
function SetWidgetVisible(widget, visible, bHitTest)
if bHitTest == nil then
bHitTest = false
end
if bHitTest then
widget:SetVisibility(visible and UE4.ESlateVisibility.Visible or UE4.ESlateVisibility.Collapsed)
else
widget:SetVisibility(visible and UE4.ESlateVisibility.SelfHitTestInvisible or UE4.ESlateVisibility.Collapsed)
end
end<hr/>C++
- 最佳IDE:Rider For Unreal 。不能debug的问题:【Rider For Unreal Debugging】。VS风格下的常用快捷键:
- 全展开Ctrl+M+X。全折叠Ctrl+M+A
- 全局搜索变量、方法名、类名Ctrl+T
- 全局搜索任意字符串Ctrl+Shift+F
- 查找引用Shift+F12
- .h文件和.cpp文件切换Ctrl+K+O
- 委托Delegate。【Unreal Engine 4:学习笔记(十)Delegate & Event】用的比较多的是DECLARE_DYNAMIC_MULTICAST_DELEGATE。注意C++绑定方法时必须是UFUNCTION。
- 代码里绑定UMG的Animation,Transient关键字不能少:
UPROPERTY(BlueprintReadOnly, Transient, meta = (BindWidgetAnim))
UWidgetAnimation* MyAnim;//名称和蓝图下的动画同名
- 在for循环里遍历字典修改元素的写法,必须加 &表示引用 :
for (auto& Item : MyDict)
{
}
- 将世界坐标转化为某个transform的相对坐标:Transform.InverseTransformPositionNoScale(FVector position)。反之是Transform.TransformPosition(FVector position)
- 将世界坐标转化为UI空间下坐标:UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPositionProjectWorldLocationToWidgetPosition(APlayerController* PlayerController, FVector WorldLocation, FVector2D& ScreenPosition, bool bPlayerViewportRelative)
- 保证分母为0也能安全不出错的除法 UKismetMathLibrary::SafeDivide
<hr/>UE4
- Super表示父类是UObject类特有的功能。
- ViewPort:游戏视口大小。比如你的电脑屏幕是1920x1080,游戏窗口占用了四分之一的屏幕。那么ViewPort就是960x540。
int32 viewPortX, viewPortY;
playController->GetViewportSize(viewPortX, viewPortY);
- DesignScreenSize:UI设计时的屏幕基准大小。比如我们设计UI时都是基于1920x1080的大小进行布局,游戏运行时的视口大小如果完全符合那么万事大吉,如果不一样则进行一定规则的缩放。【UE4 dpi scale 研究总结】
float designSizeX = viewPortX / UWidgetLayoutLibrary::GetViewportScale(this);
float designSizeY = viewPortY / UWidgetLayoutLibrary::GetViewportScale(this);
- Resolution:游戏运行的分辨率。如果是窗口模式,那么分辨率和视口大小是相等的。如果是全屏就不一定。比如你电脑1920x1080的全屏模式,依然可以把游戏分辨率选择为1280x720,此时的Resolution就是1280x720。【How to get current screen size/resolution?】
FVector2D Result = FVector2D( 1, 1 );
Result.X = GSystemResolution.ResX;
Result.Y = GSystemResolution.ResY;
- 问题:如何给粒子死亡绑定事件? ParticleSystem.OnParticleDeath.AddDynamic。同时粒子需要有Event Generator,事件类型为Death。
- Bug:资源在版本里没有加载出来。 打开Project Settings,检查Project/Packaging/Additional Asset Directies to Cook。因为引擎里没有被任何资源引用的资源默认是不会被打包进去的,所以用到了这类资源需要特殊处理。
- 崩溃:剪切粘贴Button到根节点时造成的引擎百分百崩溃。经测试Text和Image并不会造成百分百崩溃。
<hr/> UMG开发
- 问题:如何在UE4里让UMG有不同层级?
- 核心思路:调用CanvasPanelSlot的SetZOrder
UCanvasPanelSlot* slot = Cast<UCanvasPanelSlot>(MyUserWidget);
slot->SetZOrder(MyOrder);
- 首先生成一个RootPanel,然后所有需要有层级的界面都是这个RootPanel的子物体。
- ZOrder的值根据UI所在功能划分
- 问题:如何计算A控件中心相对于B控件的位置? 如图,需要计算中间这个Button距离VerticalBox的底部的距离。使用FGeometry。 注意控件的可见性不能是Hidden或者Collapsed。【How to get UMG widget absolute position in UE4】
FGeometry verticalBoxGeometry = MyVerticalBox->GetCachedGeometry();
FGeometry indicatorGeometry = MyButton->GetCachedGeometry();
auto localPosition = verticalBoxGeometry.AbsoluteToLocal(indicatorGeometry.GetAbsolutePosition()) + indicatorGeometry.GetAbsoluteSize() / 2.0f;
auto verticalBoxSize = verticalBoxGeometry.GetAbsoluteSize();
return verticalBoxSize.Y - localPosition.Y;
- 问题:如何将控件保持渲染对象大小不变而占用更少的布局? 比如需要在10x10的布局大小下显示100x100的图片。把要显示的控件放在Overlay下,调整Overlay的Padding和Transform的Translation。如图是一个在Y轴上占布局空间为0但是图片完整显示的案例,其中调整的Overlay为向顶部对齐。
- 问题:如何让列表倒着添加元素(从右往左,从下往上)?
- 修改列表控件和子物体的RenderScale皆为负数(负负得正),并在蓝图的EventConstruct事件连接(而不是PreConstruct,避免蓝图编辑时看起来是反的)。
- Bug:ScrollBox子项是Button时,不能滑动。
- 把Button的TouchMethod设为Precise Tap(精确点击)
- Bug:带参数富文本替换导致的bug。如“<字体>玩家等级{PlayerLevel}”中的PlayerLevel替换成玩家等级。
- 错误做法:更新UI的时候GetText(),查找PlayerLevel,替换玩家等级。这样做第一次是对的,第二次时再GetText()时已经没有PlayerLevel了。
- 正确做法:初始化时GetText()记录在一个数据结构,更新时从数据结构里拿。
- TreeView。使用教程:【UE4(UI)第七十三课Tree View树形结构】
- TreeView的展开收起功能是通过绑定子物体(后简称Item)的Slate事件OnMouseButtonClick来实现的,如果Item有Button则可能会冲突
- 如果要实现点击某个Item收起其他Item的功能在OnItemExpansionChanged事件调用SetItemExpansion,但是要注意调用SetItemExpansion以后收起的Item也会触发OnItemExpansionChanged事件,这里容易变成死循环。解决思路是调用SetItemExpansion前记录当前选择的是哪个Item,然后和OnItemExpansionChanged给的Item进行比较。
- 列表型控件(ListView、TileView等)的滑动条因为很丑且不能调,通常来说都要隐藏掉。在PreConstruct事件里调用SetScrollbalVisibility。
<hr/>关于作者
- 水曜日鸡,喜欢ACG的游戏程序员。曾参与索尼中国之星项目《硬核机甲》的开发。 目前在某大厂做UE4项目。
CSDN博客:https://blog.csdn.net/j756915370
知乎专栏:https://zhuanlan.zhihu.com/c_1241442143220363264
游戏同行聊天群:891809847 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|